Compare commits

...

943 Commits

Author SHA1 Message Date
6d4ca1fcc8 WorkspaceThumbnails: show attached modal dialogs togheter with their parents
The window clones in the central part of the overview are showing modal
dialogs now, and this creates an inconsistency if the thumbnail doesn't too.
Code is intentionally similar in the two places.

https://bugzilla.gnome.org/show_bug.cgi?id=650843
2013-03-04 17:50:18 +01:00
93b1af401f Workspace: show attached modal dialog
Windows in the overview should be like they appear in the workspace,
including modal dialogs that are attached above them.
In addition, hiding the dialogs in the overview causes a flash as
dialog appears at the end of the transition.

Based on a patch by Maxim Ermilov <zaspire@rambler.ru>

https://bugzilla.gnome.org/show_bug.cgi?id=650843
2013-03-04 17:50:18 +01:00
3368e49aa7 main: drop superfluous Lang.bind(this, ...)
Jasper noticed this and I forgot to clean it up before pushing,
then I just noticed it again when doing some other patches

https://bugzilla.gnome.org/show_bug.cgi?id=694326
2013-03-04 11:35:36 -05:00
9e31f05861 shellEntry: Disable copy action for password entries
Curently it is possible to copy the content of password entries,
and paste it elsewhere in clear text. This is undesirable, so
follow GTK+'s behavior and disable the copy action for password
entries.

https://bugzilla.gnome.org/show_bug.cgi?id=695104
2013-03-04 16:43:59 +01:00
b52f4ed25b st-entry: Disable cut/copy actions in password entries
Curently it is possible to copy the content of password entries,
and paste it elsewhere in clear text. This is undesirable, so
follow GTK+'s behavior and disable the cut/copy actions for
password entries.

https://bugzilla.gnome.org/show_bug.cgi?id=695104
2013-03-04 16:43:59 +01:00
00eb764880 configure.ac: remove telepathy-logger dep
We don't use it in C code any more.

https://bugzilla.gnome.org/show_bug.cgi?id=687748
2013-03-04 16:38:47 +01:00
6d011a3700 workspacesView: Always zoom out switcher when using static workspaces
We always leave the workspace switcher zoomed out when we can assume
that the user is actually making use of workspaces. For the default
dynamic workspace behavior, we make this assumption when more than
two workspaces are in use (e.g. at least two workspaces contain windows
plus an empty one at the end). However this test does not make sense
when using static workspaces - in that case, not using workspaces
would be indicated by a workspace number of 1 (in which case the
entire switcher is hidden completely), so add a check for dynamic
workspaces to the condition.

https://bugzilla.gnome.org/show_bug.cgi?id=695126
2013-03-04 15:51:25 +01:00
f9d9caf417 switcherPopup: Remove separator support
Since the alt-tab switcher stopped using separators, the functionality
is unused, remove it.

https://bugzilla.gnome.org/show_bug.cgi?id=661156
2013-03-04 15:28:38 +01:00
b50702dd52 altTab: Ignore workspaces in popups
Currently both the app switcher and the thumbnail list divide items
first into two groups (based on whether the item is located on the
current workspace or not), and then sort each group individually
by MRU.
The resulting behavior is often confusing, e.g. when using alt-tab
a second time does not switch back to the original window when the first
invocation involved a workspace switch and the workspace contains
windows of more than one application.
Instead, make the behavior more predictable by sorting both lists
strictly by MRU.

https://bugzilla.gnome.org/show_bug.cgi?id=661156
2013-03-04 15:28:37 +01:00
e74a82b9d6 Updated Spanish translation 2013-03-04 14:52:05 +01:00
12fa983242 Updated Norwegian bokmål translation 2013-03-04 14:15:59 +01:00
1607df835a Updated Galician translations 2013-03-04 12:41:17 +01:00
7b705dd670 modalDialog: Fix auto-completion of prompts immediately upon display
Shell modal dialogs can take their action on a certain key's
key-release-event. For example on <enter> the affirmative action is
usually run.

Make sure that the key was also pressed on the dialog and we're not
seeing a spurious key-release-event from a key that was pressed before
the dialog was displayed.

https://bugzilla.gnome.org/show_bug.cgi?id=692937
2013-03-04 09:17:41 +01:00
323e3028b6 Updated Brazilian Portuguese translation 2013-03-04 00:52:05 -03:00
7e1b4692e2 status/keyboard: Don't show the switcher with less than 2 items
https://bugzilla.gnome.org/show_bug.cgi?id=695000
2013-03-04 00:30:04 +01:00
c1dd971ce9 xdndHandler: Ensure that the XDnD clone is on top of the panel
The panel is added to the uiGroup, not the overlay group, so
to stack on top of it, we need to add ourselves to the uiGroup
as well.

https://bugzilla.gnome.org/show_bug.cgi?id=695073
2013-03-03 15:41:52 -05:00
39610e6933 viewSelector: Move the loupe icon to the primary slot
This matches the behavior in GTK+. See the rationale there.

https://bugzilla.gnome.org/show_bug.cgi?id=695069
2013-03-03 15:41:52 -05:00
fdb189102d Updated Russian translation 2013-03-03 23:22:11 +04:00
e54d095064 OtherUsersDialog: wrap the description label
In some languages (like Italian) it doesn't fit in one line.

https://bugzilla.gnome.org/show_bug.cgi?id=695002
2013-03-03 13:51:30 +01:00
e6634c56d3 OtherUsersDialog: don't show closing sessions
It is possible that a session survives after closing, if any processes
(usually PulseAudio and GConf) are still alive at the end. In that case,
ignore it, as the user is already logged out and there is nothing to lose.

https://bugzilla.gnome.org/show_bug.cgi?id=695002
2013-03-03 13:51:30 +01:00
deb77a4dde switcherPopup: Cancel the OSD popup before showing
It is irritating to show two different system popups at the same
time, so let switcher popups like alt-tab cancel out the OSD.

https://bugzilla.gnome.org/show_bug.cgi?id=613543
2013-03-03 12:59:09 +01:00
90ea27c423 shellDBus: Export ShowOSD() method on the bus
Export a simple method to trigger an OSD popup. gnome-settings-daemon
will use this to replace its own OSD UI.

https://bugzilla.gnome.org/show_bug.cgi?id=613543
2013-03-03 12:59:09 +01:00
19533f87e3 osdWindow: Add a simple OSD popup
Implement a basic OSD popup that shows an icon and optionally a label
and a fill level. It is based on the existing OSD implementation in
gnome-settings-daemon, which it will replace.

https://bugzilla.gnome.org/show_bug.cgi?id=613543
2013-03-03 12:59:08 +01:00
e6c5c84133 Updated Greek translation 2013-03-03 13:24:37 +02:00
404aaa5658 Updated Uyghur translation
Signed-off-by: Gheyret Kenji <gheyret@gmail.com>
2013-03-03 16:00:53 +09:00
2c70df25f5 Updated Vietnamese translation 2013-03-03 11:13:32 +07:00
db959ac2eb po/vi: import from Damned Lies 2013-03-03 11:11:26 +07:00
bbf0dce390 Punjabi: Translation updated (aalam) 2013-03-03 07:10:45 +05:30
1cbb8b9851 layout: tweak startup animation
Following designer feedback, this commit makes the startup
animation a little more subtle.

https://bugzilla.gnome.org/show_bug.cgi?id=694326
2013-03-02 17:44:26 -05:00
96387cce47 background: drop StillFrame class
We aren't using it anymore so nix it.

https://bugzilla.gnome.org/show_bug.cgi?id=694326
2013-03-02 17:44:26 -05:00
570fc68cb1 main: put noise texture below startup animation, not still frames
Right now we take a still frame of the desktop before showing the
start up animation.  This gives us an animation over what was
there before startup.

That's not actually desirable when restarting the shell.  We don't
want to animate over undecorated windows, we really want to animate
over the noise texture.

This commit drops the still frames in favor of the noise texture.

https://bugzilla.gnome.org/show_bug.cgi?id=694326
2013-03-02 17:44:26 -05:00
e6ce0057af screenShield: only pop lock-screen mode at the end of lift
This avoids the style of the top panel changing as soon as the lift key
is pressed weakening the curtain effect.

https://bugzilla.gnome.org/show_bug.cgi?id=695023
2013-03-02 17:26:28 -05:00
a3f625fe39 screenshot: Discard invalid sized areas
https://bugzilla.gnome.org/show_bug.cgi?id=694998
2013-03-02 22:26:27 +01:00
bdfe459d64 css: Make input sources switcher symbol font smaller
Particularly when using asian languages the symbol could become large
enough to not fit in the space we have and we'd end up with a totally
ellipsized item.

https://bugzilla.gnome.org/show_bug.cgi?id=695001
2013-03-02 18:42:59 +01:00
95ec8ef5e1 shell-global: Fix regression from 071a4e5f83
The clutter_threads_add_repaint_func_full need to return TRUE otherwise
they will be only called once.

https://bugzilla.gnome.org/show_bug.cgi?id=695006
2013-03-02 18:25:34 +01:00
b8198716d9 l10n: Updated Italian translation 2013-03-02 15:55:04 +01:00
071a4e5f83 shell-global: Don't connect to the stage's paint signal
Doing so causes useless full stage redraws and breaks culling
as clutter cannot know how the signal handler affects painting.

So use clutter_threads_add_repaint_func_full instead.

https://bugzilla.gnome.org/show_bug.cgi?id=694988
2013-03-02 14:43:11 +01:00
d99cd71408 Updated POTFILES.in 2013-03-02 01:31:00 +01:00
030e6aa507 Fix overview sizing with multimonitor
Previously, the overview BoxLayout was sized and positioned explicitly
using the primary monitor coordinates, as its parent was at 0,0 and
with a fixed layout manager. Now we use a bin layout, so we need to
position and size the stack actor, and set the overview boxlayout to
expand.
Take also the occasion to use a MonitorConstraint instead of handling
position and size manually.

https://bugzilla.gnome.org/show_bug.cgi?id=694969
2013-03-02 00:29:13 +01:00
f146b01e3e Fix dash sliding animation with multiple monitors
We used to clip the overview group to prevent the dash from sliding into
neighbor monitors, but now it moved to the groupStack, so we must move
the clip too.

https://bugzilla.gnome.org/show_bug.cgi?id=694970
2013-03-02 00:26:39 +01:00
c249ff9046 theme: tone down search results description font style
- don't use bold
- use regular font size
- make the text shadow less dramatic
2013-03-01 14:21:07 -05:00
8b48560c81 overviewControls: add a new messages indicator
The new messages indicator will be visible when there are non-resident
notifications in the tray.

https://bugzilla.gnome.org/show_bug.cgi?id=687787
2013-03-01 13:50:51 -05:00
bdbea2463b messageTray: add an indicatorCount property
This filters out resident and transient notifications in the normal
case, but just returns the number of unread messages for the Telepathy
implementation.

https://bugzilla.gnome.org/show_bug.cgi?id=687787
2013-03-01 13:17:51 -05:00
8fb2263471 messageTray: emit a signal when we're showing or hiding
This will be useful in a future commit.

https://bugzilla.gnome.org/show_bug.cgi?id=687787
2013-03-01 13:17:51 -05:00
ea55c36a3a st-widget: Don't add hidden actors to the paint volume
We are not painting them anyway.
2013-03-01 19:13:22 +01:00
37595ff3e7 shell-generic-container: Exclude skip_paint actors from the paint volume
We are not painting them so there is no reason to redraw a bigger
area because of them.
2013-03-01 19:13:10 +01:00
cca008b73c overview: Move dash to a separate layer
We generally want view content centered, in particular where the
view itself is symmetrical. So move the dash to a separate layer
and use a placeholder to account for its size when showing the
window picker, which is the only view where it doesn't make sense
to center the content.

https://bugzilla.gnome.org/show_bug.cgi?id=694261
2013-03-01 18:25:53 +01:00
b9dcbd9d33 overview: Use the new main actor in show/hide transitions
Commit 43ed66cf26 changed the toplevel overview actor, so it
makes sense to change the show/hide transitions to use that instead
of the existing group, to avoid elements layered on top of the
group being excluded from the transitions.
2013-03-01 18:25:53 +01:00
b6bf8d5b2d Layout: don't minimize fullscreen windows that are focused
Windows can sometimes be focused, or appear to be focused, without being
at the exact top of the stack, for example in the case of override-redirect
windows, or with attached modal dialogs. In that case, we should not
try to minimize them (as it creates a loop that makes it impossible to restore
the window)

https://bugzilla.gnome.org/show_bug.cgi?id=694905
2013-03-01 18:07:47 +01:00
43ed66cf26 overview: use a bin for the main overview actor
We'll pack the new messages indicator on top of the current overview
group, so we need to pack it into a ClutterBinLayout first.

https://bugzilla.gnome.org/show_bug.cgi?id=687787
2013-03-01 11:34:09 -05:00
57eae1be43 main: register with session manager explicitly
Mutter now makes session registration an explicit required
step.  This is so we can tell the session manager when
we're ready to move on to the next phase.

This commit calls the new Meta.register_with_session() api after we're
initialized.

https://bugzilla.gnome.org/show_bug.cgi?id=694876
2013-03-01 11:31:18 -05:00
b394d184cc scroll-view-fade: Tweak easing at edges
Since commit b4f5f1e461, the effect is eased in at the scroll
view's edges so that it does not appear out of nowhere. However,
the linear easing used is not the best option, as now the effect
appears so late that content near the edges ends up just being
cut off rather than faded out.
So adjust the easing function to have the effect appear faster.

https://bugzilla.gnome.org/show_bug.cgi?id=694327
2013-03-01 17:30:01 +01:00
81e01b6f88 shellDBus: Add methods to handle key grabs
Expose Mutter's external grab API on the bus, so gnome-settings-daemon
can refer keygrabbing to the shell.

https://bugzilla.gnome.org/show_bug.cgi?id=643111
2013-03-01 16:14:30 +01:00
52efe32e0f Updated Basque language 2013-03-01 09:27:53 +01:00
e7886734c4 search: fix description of results
"description" is documented as a valid field for search result metas,
and ListSearchResults implements it, so pass it down to be used.
Also, don't wrap the description in quotes, so that the search provider
can decide if it is an excerpt from the searched text or something else.
And to that extent, set use_markup to true, so that terms can be
highlighted.

https://bugzilla.gnome.org/show_bug.cgi?id=694906
2013-03-01 00:38:50 +01:00
083c37a7b2 workspace: Fix bad window positions with small windows
This can happen if you open two or three terminal windows, and then
open the overview -- they're not centered. The issue is that because
of the WINDOW_CLONE_MAXIMUM_SCALE clamping, the scale that is being
laid out is different from the scale that the layout was calculated
for.

Implement and document a hack-ish solution which simply keeps the
scale for the layout as originally calculated, but centers the
windows inside the cell.

https://bugzilla.gnome.org/show_bug.cgi?id=694902
2013-02-28 18:15:15 -05:00
b24a10aa00 workspace: Use the scaled width when calculating the total width
This is a small inconsistency I found in the code after review.

https://bugzilla.gnome.org/show_bug.cgi?id=694902
2013-02-28 18:13:50 -05:00
e1ef14d12b workspace: Write a giant wall of text describing the algorithm
I wrote this for me to find solutions to some problems we were having
with the current situation, so why not check it in?

https://bugzilla.gnome.org/show_bug.cgi?id=694902
2013-02-28 18:13:50 -05:00
1a33de91e2 workspace: Separate out per-window scaling as a separate variable
Multiplication is linear, so we can split this out as a separate
component. This will make it easier to think of it as an additional
per-window scaling factor, rather than tweaking the scale a bit,
which is more correct to the model.

https://bugzilla.gnome.org/show_bug.cgi?id=694902
2013-02-28 16:26:09 -05:00
6e15e2d72a workspace: Remove unused variable
This typo seems to have been here since the very beginning;
no "state" variable appears anywhere.

https://bugzilla.gnome.org/show_bug.cgi?id=694902
2013-02-28 16:26:08 -05:00
d58b715c52 workspace: Remove some additional features used for GridLayoutStrategy
While we won't tear down the entire strategy infrastructure, we want to
rework some layout code in the future, so just tear this piece out for
now.

https://bugzilla.gnome.org/show_bug.cgi?id=694902
2013-02-28 16:26:08 -05:00
804ff8b5a5 layout: Mark the correct monitor as fullscreen 2013-02-28 13:34:05 -05:00
6e89d2f46a extensionPrefs: Fix opening extension preferences on startup
gnome-shell-extension-prefs supports opening a specific extension's
preferences directly from the command line by passing the UUID.
However this broke when extension loading was changed to be processed
asynchronously, as no extension has been loaded when the command
line argument is processed. Fix by deferring opening the extension's
preferences until all extensions have been loaded.

https://bugzilla.gnome.org/show_bug.cgi?id=694858
2013-02-28 15:37:29 +01:00
e99d69b7d9 extensionUtil: Add ExtensionFinder::extensions-loaded signal
Consumers might want to defer work until the initial loading of
extensions has finished, so add an appropriate signal.

https://bugzilla.gnome.org/show_bug.cgi?id=694858
2013-02-28 15:37:28 +01:00
f25416c3f5 Updated Greek translation 2013-02-28 10:10:11 +02:00
a7bb6a2781 Show the session list when needed
Some of the conditions for showing the user list were not
properly inverted, causing the session list to be hidden
when it shouldn't be and shown when it shouldn't be.

https://bugzilla.gnome.org/show_bug.cgi?id=694784
2013-02-27 18:09:43 -05:00
41f14e0e89 layout: Trigger the tray if one event passes the threshold
As a special-case to the "cap event" rules, this allows a heavy swipe
from top to bottom to allow triggering the tray without having to push
into it.

https://bugzilla.gnome.org/show_bug.cgi?id=694467
2013-02-27 14:01:42 -05:00
01bd10f485 layout: Cap each event to 15px of motion
When pressing against the bottom of the screen, we shouldn't
really take more than 15px from each event, to prevent spruious
mouse movements from opening the barrier.

https://bugzilla.gnome.org/show_bug.cgi?id=694467
2013-02-27 14:01:42 -05:00
476eacd5ca layout: Adjust the pressure barrier threshold values
After a day of experimentation with Cosimo and Jon McCann, we
found that this was the best way to do this.

https://bugzilla.gnome.org/show_bug.cgi?id=694467
2013-02-27 14:01:42 -05:00
ca2fb74f41 Updated Traditional Chinese translation(Hong Kong and Taiwan) 2013-02-27 22:59:02 +08:00
8a2baf5b6e Updated Vietnamese translation 2013-02-26 20:47:16 +07:00
d44fc3aa1b po/vi: import from Damned Lies 2013-02-26 20:34:33 +07:00
7d78c42dfc workspaceThumbnails: remove custom scrolling code
Now that we listen to scroll event on the whole overview group, this
makes the thumbnail switcher scroll twice per-event.
2013-02-25 18:23:42 -05:00
a361180745 layout: Handle _NET_WM_FULLSCREEN_MONITORS
Mark all monitors that fullscreen when window is using
the _NET_WM_FULLSCREEN_MONITORS to be fullscreen on multiple monitors.

https://bugzilla.gnome.org/show_bug.cgi?id=646861
2013-02-25 22:06:45 +01:00
310dc10c4d Center the search results as well
Use overlay_scrollbars and the same padding.

https://bugzilla.gnome.org/show_bug.cgi?id=694701
2013-02-25 14:49:25 -05:00
96e02c4c2e tweener: Look at the XSetting for disabling animations
gnome-settings-daemon will be changed to override the XSetting in
the case where we're on a remote display rather than overwriting a
user setting, so we need to look at the XSetting here.

https://bugzilla.gnome.org/show_bug.cgi?id=694320
2013-02-25 12:59:35 -05:00
afb3b9f029 Updated Norwegian bokmål translation 2013-02-25 18:35:23 +01:00
d5e647a191 ScreenShield: fix positioning of background with multimonitor
Previously, we would create one StBin per monitor, but each was positioned
at 0,0 and sized as the screen, so they would overlap and draw the box shadows
on top of the other backgrounds.
Instead, we need to size appropriately the bin, and then we need to position
the actual MetaBacgroundActor at 0,0, so add a flag to BackgroundManager
for this.
Also, get rid of MetaBackgroundGroup, they do nothing because the screenshield
is not a descendant of the MetaWindowGroup and because the widget in between
blocks the propagation of the visible region. At the same time, use a
widget, not a bin, because StBin requires you to set .child, not call add_child().

https://bugzilla.gnome.org/show_bug.cgi?id=694394
2013-02-25 14:46:40 +01:00
a1d37617a8 Fixed typo in string. Fixes bug #687248 2013-02-25 14:27:31 +01:00
2541f85942 Updated gujarati file 2013-02-25 16:39:49 +05:30
07fd23dc5e Updated Slovenian translation 2013-02-24 21:34:43 +01:00
946311b2a3 Updated Polish translation 2013-02-24 01:55:12 +01:00
4868032215 Updated Czech translation 2013-02-24 01:18:25 +01:00
fb0cf64536 viewSelector: Don't focus search entry on whitespace
We recently started to trim leading and trailing whitespace from
the search string, and not to trigger a search when the resulting
string was empty. However we still allow whitespace to trigger
type-ahead-find, so that the key focus is moved briefly to the
search entry and back to the stage, resulting in a disruptive
flickering of the entry.
Fix this by excluding whitespace from triggering type-ahead-find.

https://bugzilla.gnome.org/show_bug.cgi?id=694475
2013-02-23 09:42:09 +01:00
b37afcdba1 xdnd: Only pick reactive actors
This is the only sane way to get select windows in the overview,
as workspace._dropRect blocks our view otherwise.
2013-02-22 20:02:22 +01:00
f644bee831 xdnd: Don't wait for the stage to be disabled when it is already visible
xdndHandler used to wait for the stage to show up before calling
global.init_xdnd() . Since commit 65303d027a xdndHandler is initalized
later so the stage might be already visible at this point causing the
show signal never to be emitted and thus global.init_xdnd will never
be called.

Fix that by checking by calling global.init_xdnd without waiting for
the stage's show signal to be emitted, as the stage is guaranted to be
visible at this point.
2013-02-22 19:37:40 +01:00
6fcc7e3e23 Background: don't check draw-background key
The key was removed from gsettings-desktop-schemas, GNOME should
always draw a background: the key existed to support xsetroot, but
we no longer read the _XROOTPMAP_ID.

https://bugzilla.gnome.org/show_bug.cgi?id=694463
2013-02-22 17:49:16 +01:00
95602eb85d AppDisplay: fix allocation loop warnings
Don't use a constraint to position the boxpointer horizontally. Instead,
use the right alignment flags and let the layout managers do their job.

https://bugzilla.gnome.org/show_bug.cgi?id=694284
2013-02-22 14:58:13 +01:00
9f3afdf928 windowManager: Don't use show_all
It is deprecated and does not make sense there anyway, so use show() instead.

https://bugzilla.gnome.org/show_bug.cgi?id=694441
2013-02-22 14:28:58 +01:00
627a2412d2 ui: Don't use Clutter.Group
It is deprecated use Clutter.Actor instead.

https://bugzilla.gnome.org/show_bug.cgi?id=694441
2013-02-22 14:28:58 +01:00
acffd1e792 main: Guard against NULL log_domain in default handler
g_str_has_prefix() will assert when passed NULL, so we need to make
sure that we are passed a non-NULL log_domain first.

Spotted by <goughost<at>yahoo.com.cn>

https://bugzilla.gnome.org/show_bug.cgi?id=663601
2013-02-22 11:02:00 +01:00
62ca4ba624 appDisplay: Apply the same padding to AllView and FrequentView
We add some horizontal padding to the AllView's content to make
sure content does not end up underneath the scrollbar; while this
is not required in case of the FrequentView which does not have
a scrollbar, applying the same padding ensures that both views
end up with the same spacing, which makes switching between them
less disruptive.

https://bugzilla.gnome.org/show_bug.cgi?id=694261
2013-02-22 10:57:50 +01:00
6ea8f35343 appDisplay: Center AllView content
If the AllView is scrolled, the vertical scrollbar will take away
some horizontal space on one side, resulting in the content ending
up slightly off-center.
Account for this by using overlay-scrollbars instead.

https://bugzilla.gnome.org/show_bug.cgi?id=694261
2013-02-22 10:57:50 +01:00
1bd8c67041 st-scroll-view: Add :overlay-scrollbars property
If enabled, scrollbars take away from the allocation given to the
view's content. This is usually preferrable to painting the bars on
top of the content, but there are exceptions, for instance when the
content needs to be centered with regard to the view as a whole.
Add a :overlay-scrollbars property to account for those cases.

https://bugzilla.gnome.org/show_bug.cgi?id=694261
2013-02-22 10:57:50 +01:00
7425b382d6 appDisplay: Remove scroll view hack in FrequentView
The frequent view should not be scrolled, but to work around the
icon grid overflowing, we used a (non-scrolling) scroll view anyway.
Now that IconGrid:fillParent allows us to avoid overflow, we can
remove this hack.

https://bugzilla.gnome.org/show_bug.cgi?id=694256
2013-02-22 09:50:57 +01:00
5f61b57d63 iconGrid: Add fillParent property
Setting this property will only display as many icons as fit the parent's
allocation, rather than overflowing.

https://bugzilla.gnome.org/show_bug.cgi?id=694256
2013-02-22 09:46:30 +01:00
9db73767d9 appDisplay: Remove unnecessary StBin from hierarchy
The bin is used to top-align views that don't fill the entire height,
but the same can be achieved by setting appropriate expand properties.
2013-02-22 00:07:54 +01:00
c562245c16 main: don't show stage until still frames are loaded
We sometimes map the stage before we've loaded a background on it
because of a race asynchronously loading the session mode.

This manifests as the startup animating starting over a white
background.

This commit defers showing the stage until after the still frames
are loaded.

https://bugzilla.gnome.org/show_bug.cgi?id=694321
2013-02-21 15:01:39 -05:00
9525216d78 messageTray: Fix warning when opening the tray
As the tray hover handler will call _cancelTrayDwell, we need to
ensure this variable is initialized even if tray dwelling isn't.

https://bugzilla.gnome.org/show_bug.cgi?id=694336
2013-02-21 11:56:33 -05:00
28a71a29e6 Revert "messageTray: Fix warning when opening the tray"
This reverts commit 7b06d34ba4.

https://bugzilla.gnome.org/show_bug.cgi?id=694336
2013-02-21 11:52:20 -05:00
7b06d34ba4 messageTray: Fix warning when opening the tray
As the tray hover handler will call _cancelTrayDwell, we need to
ensure this variable is initialized even if tray dwelling isn't.

https://bugzilla.gnome.org/show_bug.cgi?id=694336
2013-02-21 11:40:49 -05:00
656d24e477 overviewControls: don't set padding on the whole overview group
Set it on the control slider actor instead. At the same time, remove
bottom padding from search results actor.

https://bugzilla.gnome.org/show_bug.cgi?id=694287
2013-02-21 11:37:44 -05:00
df0f03d831 overview: Keep open when a Control key is held
It is useful at times to perform several actions that would usually
close the overview (for instance launching an application) at once.
Currently we allow this by dragging items to a workspace rather than
just clicking it, but it's an odd metaphor with its own set of
problems.
Introduce an alternative approach (inspired by file selection in
file managers) by keeping the overview open if a Control key is
held down.

https://bugzilla.gnome.org/show_bug.cgi?id=686984
2013-02-21 17:21:54 +01:00
1db6d15677 overviewControls: fade view selector and thumbnails slider on DnD
When an item is dragged from a page that is not the windows one, the
only possible target is the dash, so fade out the rest.

https://bugzilla.gnome.org/show_bug.cgi?id=686984
2013-02-21 11:16:43 -05:00
08a0479c9e WorkspaceThumbnail: fix mutter warning
Pass a proper timestamp to workspace activation functions

https://bugzilla.gnome.org/show_bug.cgi?id=694365
2013-02-21 16:10:09 +01:00
6682b7dfa5 Updated Russian translation 2013-02-21 18:22:27 +04:00
aad5d98b43 iconGrid: Take extra spacing into account for height requests
This fixes the clipped app picker in the overview.

https://bugzilla.gnome.org/show_bug.cgi?id=694234
2013-02-20 19:38:38 -05:00
a8a4a85dac lg: add a reference to lookingGlass in the Extensions tab.
When the user clicks on "View Source" or "Web Page" in the "Extensions" tab of
looking glass, the callback _onViewSource() or _onWebPage() is called and they
try to close looking glass: this._lookingGlass.close();

But it does not work and generate the exception "this._lookingGlass is
undefined". This patch fixes that.

https://bugzilla.gnome.org/show_bug.cgi?id=693814
2013-02-20 23:14:53 +00:00
ad71b969b2 Workspace: fix stacking of window clones
We must set the stack above property of window clones, or they will
not stack themselves properly when a drag is cancelled.

https://bugzilla.gnome.org/show_bug.cgi?id=694292
2013-02-20 20:56:58 +01:00
8bcb10391e dash: add a workaround for Clutter bug 692744
https://bugzilla.gnome.org/show_bug.cgi?id=690643
2013-02-20 20:51:11 +01:00
8e7d74bc3b dash: fix icon animation
animateIn and animateOut should not reset the scale, otherwise
extra animate calls (which are possible because the diff algorithm
in _redisplay is not optimal) cause unneeded movement.
Therefore, create new items hidden, and have the creator call
animateIn or set the scale/opacity properties manually.
adjustIconSize must be changed to always set the icon size temporarily,
otherwise the first time it computes the icon size with 0 scale.

https://bugzilla.gnome.org/show_bug.cgi?id=690643
2013-02-20 20:51:11 +01:00
629b6faa22 dash: fix allocation loop when increasing icon size
The DashActor will known to allocate the show apps button only if the
icon size is (temporarily) too big for the containing box, therefore
it should request just that as the minimum size.
This solves a glitch that happened when removing a favorite and at the
same time causing the dash to expand.

https://bugzilla.gnome.org/show_bug.cgi?id=690643
2013-02-20 20:51:11 +01:00
ef1e27966d dash: turn DashItemContainer into a proper St.Widget
This removes a number of unneeded ._delegate accesses and cleans up
the code.

https://bugzilla.gnome.org/show_bug.cgi?id=690643
2013-02-20 20:51:11 +01:00
ea0eb4ba09 Bump version to 3.7.90
Update NEWS.
2013-02-20 20:18:00 +01:00
f83ad77c08 workspacesView: Remove some unused variables
More left-overs from the custom swipe-scrolling code.

https://bugzilla.gnome.org/show_bug.cgi?id=686639
2013-02-20 20:17:20 +01:00
af219ce9e7 workspacesView: Allow workspace switching via scroll wheel
This is already possible over the workspace switcher, extend this
behavior to the entire window picker area.

https://bugzilla.gnome.org/show_bug.cgi?id=686639
2013-02-20 20:17:20 +01:00
2ac88a7fa6 overview: Remove some unused variables
Those are left-overs from the custom swipe-scroll implementation.

https://bugzilla.gnome.org/show_bug.cgi?id=686639
2013-02-20 20:17:19 +01:00
c495ff8ad8 overview: Expose scroll-events
Using the scroll wheel in the window picker should switch workspaces.
As the picker doesn't have a visible boundary though, it makes sense
to accept scroll-event for the entire overview area. Rather than
making the overview's main actor public, expose scroll-events via
a signal.

https://bugzilla.gnome.org/show_bug.cgi?id=686639
2013-02-20 20:17:19 +01:00
0e12215614 workspace: Remove zooming of window previews
Using the scroll wheel to switch workspaces makes sense, but it is
currently only supported on the workspace switcher, as it conflicts
with window zooming in the picker.
As changing workspaces is far more useful than the zoom feature,
remove the latter in order to "free" the scroll wheel for workspace
switching.

https://bugzilla.gnome.org/show_bug.cgi?id=686639
2013-02-20 20:17:19 +01:00
b4b13b0cb9 endSessionDialog: don't process response until closed
Right now if a user logs out, they are thrown to the login screen
imediately, without even seeing the dialog close.

This commit fades the dialog out before processing the logout.

https://bugzilla.gnome.org/show_bug.cgi?id=694296
2013-02-20 14:16:54 -05:00
677cdfd6c8 modalDialog: emit 'closed' signal when dialog is closed
Opening and closing a modal dialog is not instant. There
is a transition animation involved.  When the dialog is
finished opening, it currently emits the "opened" signal.

When it's finished closing, however, it doesn't emit a
"closed" signal.  This means, there's not a good way to
know when the dialog finishes closing.

This commit adds the "closed" signal.

https://bugzilla.gnome.org/show_bug.cgi?id=694296
2013-02-20 14:16:53 -05:00
e9f18c6d8d modalDialog: drop global.get_current_time() arguments to close
Modal dialogs take an optional timestamp in their close method.
If the timestamp is not passed in, then global.get_current_time()
is used.

Some callers of the close method pass in global.get_current_time()
unnecessarly (since it's the default).

This commit drops the argument for those cases.

https://bugzilla.gnome.org/show_bug.cgi?id=694296
2013-02-20 14:16:53 -05:00
8640e18d1f backgroundMenu: Rename "System Settings" to "Settings"
And reverse menu order in order to avoid it reading "Change Background… Settings"

"System Settings" shouldn't be used because it's not used to mean the control-center in any other place.

https://bugzilla.gnome.org/show_bug.cgi?id=694290
2013-02-20 20:07:08 +01:00
300c4d8432 gdm: make entry reactive when asking user question
We disable the entry after a user answers a question while we
process it, but we don't reactivate it later if asked another
one.

This commit makes sure the entry is always reactive when we
are waiting for an answer from the user.

https://bugzilla.gnome.org/show_bug.cgi?id=691806
2013-02-20 13:38:00 -05:00
92c877493a theme: Remove some unused css classes 2013-02-20 18:10:59 +01:00
ff4926be35 theme: remove unused references to dash-search-button
The search provider buttons have been removed, so we don't need
their styling in the css.

https://bugzilla.gnome.org/show_bug.cgi?id=694289
2013-02-20 16:55:32 +00:00
df3872f665 theme: use standard button styling for the app view switcher
The switcher widget for all / frequent applications should use the
standand button styling. This is more consistent.

https://bugzilla.gnome.org/show_bug.cgi?id=694265
2013-02-20 17:46:51 +01:00
fcded2ea2b appDisplay: Fix view-switcher buttons in RTL locales
https://bugzilla.gnome.org/show_bug.cgi?id=694265
2013-02-20 17:46:51 +01:00
420f0109ee workspacesView: Treat 'gesture-cancel' the same as 'gesture-end'
When a swipe scroll completes, we scroll the active workspace into
view. This works fine if the gesture is completed properly by
emitting 'gesture-end', but not when Clutter considers the gesture
cancelled - in that case, the view remains stuck in an intermediate
position. To fix, treat 'gesture-cancel' the same as 'gesture-end'.

https://bugzilla.gnome.org/show_bug.cgi?id=689394
2013-02-20 17:43:44 +01:00
b4678a1cee layout: Clean up consoleBackgroundGroup
We shouldn't leave a reference to a destroyed actor just hanging
around.

https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-20 11:36:19 -05:00
5d3c90197c layout: Rebuild struts and the input region when the Shell starts up
This means that windows will be positioned correctly with respect
to the panel when the shell starts up, and there won't be adjusting
after the session animation zooms in entirely.

https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-20 11:36:19 -05:00
d5d517748c layout: Remove freezeUpdateRegions/thawUpdateRegions
Due to a bad rebase causing freezeUpdateCount to never get initialized,
these functions effectively did nothing. Since we're going to go to a
different mechanism for freezing region updates, let's just tear these
out now instead of fixing them before tearing them out.

https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-20 11:36:19 -05:00
de3d3c15a5 layout: Don't bother updating the regions in the greeter
The greeter is always full-screen, so this should never matter.

https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-20 11:36:18 -05:00
3db253998b panel: Don't try to begin a grab op while something else has taken a grab
https://bugzilla.gnome.org/show_bug.cgi?id=694241
2013-02-20 11:36:06 -05:00
d3f978a19d AppDisplay: fix undefined variable reference
https://bugzilla.gnome.org/show_bug.cgi?id=694282
2013-02-20 17:04:01 +01:00
a50ddd6d8c AppDisplay: fake a release event to dnd when opening the menu
PopupMenuManager eats the next release event, which would otherwise close
the menu, so we need to tell dnd that we're handling it, and no drag should
be started, so it can ungrab the pointer and restore state.

https://bugzilla.gnome.org/show_bug.cgi?id=694276
2013-02-20 17:01:25 +01:00
37700b4b1a AppDisplay: fix folder popup arrow
The arrow origin must be in the coordinate space of the box pointer,
but it cannot be calculated when the box pointer is shown, because it's
not allocated yet, so pass the source to the boxpointer and adjust
the arrow at the next paint.

https://bugzilla.gnome.org/show_bug.cgi?id=694264
2013-02-20 16:04:54 +01:00
a8f9871725 keyboard: destroy actors when a group is removed
We can't reuse key actors since signals connected to them are
associated to the original group object.
Fix of commit 824fbe09c2.

https://bugzilla.gnome.org/show_bug.cgi?id=681735
2013-02-20 22:39:41 +09:00
a4a5492782 st-bin: fix a typo in the property setter
This got unnoticed for more than 3 years!

https://bugzilla.gnome.org/show_bug.cgi?id=694234
2013-02-20 00:06:52 -05:00
9ebb80502a background: don't call destroy() from the destroy handler
We're calling destroy() in a callback of the destroy handler, which
results in an X server crash at login.

https://bugzilla.gnome.org/show_bug.cgi?id=694240
2013-02-20 00:06:38 -05:00
dcacbb37c3 background: destroy actors don't remove them from their parent
This reads better, and it also fixes a warning when background
is changed while transitioning to the overview.

https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-19 21:54:01 -05:00
34443da7ab screenShield: don't use Bin for BackgroundManager container
St.Bin() really only expects one child at a time, and the
BackgroundManager will add two. This can cause assertion
failures when destroying one of the background actors.

https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-19 21:54:00 -05:00
09c14f1a10 background: s/NULL/null/
woops.
2013-02-19 21:08:27 -05:00
e801647083 layout: Fix some indentation 2013-02-19 21:00:46 -05:00
0122cee0ae Background: ignore cancellation errors
If the request cancellable is cancelled, the requesting background was
destroyed, so don't bother reporting the result.

https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-19 20:57:21 -05:00
1188b1be36 layout: Hide the message tray and OSK during the startup animation
https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-19 20:57:21 -05:00
905c4932d1 layout: invert isGreeter check
I somehow lost an exclamation point.

https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-19 20:57:21 -05:00
6484da26f0 layout: add back startingUp state variable
I dropped it on accident during last minute refactoring

https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-19 20:57:21 -05:00
079041cf1f BackgroundMenu: fix regression from new background handling
BackgroundMenu must ensure the actor it attaches to is reactive, and
the layout manager must create a background menu for the first background
too.

https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-19 20:57:21 -05:00
b8df7798bf Punjabi: Translation updated (aalam) 2013-02-20 01:37:24 +00:00
e4af954a95 LayoutManager: add missing constant
https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-20 02:25:36 +01:00
f3df62f27e WorkspaceThumbnail: fix typo in stacking code
Was causing thumbnails to be below the background.

https://bugzilla.gnome.org/show_bug.cgi?id=694227
2013-02-20 01:59:14 +01:00
4b3c9e826d workspace: Don't use grid layout
Right now we arrange the window thumbnails in a grid if there are more than two
rows of them. This was originally intended to reduce the amount of noise in the
thumbnail arrangement. It also made sense when the thumbnails were of a similar
size.

Nowadays we reflect the size of windows in the size of the thumbnail. This
leads to huge amounts of space between some windows when they are grid aligned.
Removing the grid alignment would also lead to larger thumbnails.

https://bugzilla.gnome.org/show_bug.cgi?id=694210
2013-02-20 00:44:37 +01:00
ba4780cbd3 layout: don't bother hiding window group
Right now we hide the window group if we're a greeter.
We did this to avoid showing the background. These days
we don't add the background in the first place, so we
can drop this code.

https://bugzilla.gnome.org/show_bug.cgi?id=682429
2013-02-19 18:39:54 -05:00
b1a6940188 background: add slide show support
gnome-desktop's background drawing code supports an
XML format for presenting backgrounds based on time of day,
monitor geometry, etc. Now that we don't use gnome-desktop for drawing the
background, we need to implement that support ourselves to maintain
feature parity.

This commit implements that.

https://bugzilla.gnome.org/show_bug.cgi?id=682429
2013-02-19 18:39:54 -05:00
3c8325f1f3 layout: rework background handling
This commit updates the code to use mutter's new background
api, and changes the shell's startup animation to be closer
to the mockups.

Based on initial work by Giovanni Campagna

https://bugzilla.gnome.org/show_bug.cgi?id=682429
2013-02-19 18:39:54 -05:00
5fa9581db3 loginDialog: add cross fade animation between states
This commit adds a crossfade between the user selection state
and the user verification state.

https://bugzilla.gnome.org/show_bug.cgi?id=694062
2013-02-19 18:39:54 -05:00
96001d86e1 loginDialog: put "Not Listed?" button and user list in separate container
The user list and the "Not Listed?" button get shown and hidden at the
same time, so we can simplify the code by putting them in a new
subcontainer.

This commit creates a userSelectionBox container that both actors get
put in, and changes all the code that shows and hides these actors to
show and hide userSelectionBox instead.

https://bugzilla.gnome.org/show_bug.cgi?id=694062
2013-02-19 18:39:54 -05:00
963e808d98 loginDialog: move prompt hiding code to hidePrompt
The sessionList and the prompt hint are all really
part of the prompt, so we should have the code that
hides those things in hidePrompt instead of in
showUserList.

This commit does that.

https://bugzilla.gnome.org/show_bug.cgi?id=694062
2013-02-19 18:39:54 -05:00
d124ca377f loginDialog: use user widget when doing verification
Right now, when a user item is clicked we remove all other users from
the list and position the item in the appropriate place on screen.

Ultimately, we're going to want to crossfade from the fully populated
list to the user prompt.  Since we're going to need to show the user
avatar in two different positions we can't simply move it.

This commit leaves the user item for the user list, and instead shows
a UserWidget actor during user verification, in the same way the
unlock dialog shows a UserWidget actor during reauthentication.

https://bugzilla.gnome.org/show_bug.cgi?id=694062
2013-02-19 18:39:53 -05:00
a3d3d81447 unlockDialog: move user widget into separate file
The user widget is the username and avatar shown on
the unlock dialog.

The login dialog has something very similar.

This commit separates the user widget out to its own
file, so we can use it from the login dialog in a
later commit.

https://bugzilla.gnome.org/show_bug.cgi?id=694062
2013-02-19 18:39:53 -05:00
244121d920 loginDialog: fade out before starting session
Right now we very abruptly kill the login screen
and start the users session without any transition
out.

This commit introduces a fade out of the dialog and
panels.

https://bugzilla.gnome.org/show_bug.cgi?id=694062
2013-02-19 18:39:53 -05:00
adf95fb90d loginDialog: drop animations
The latest mockups don't animate between states by
resizing actors. Instead, crossfades are employed.

This commit strips out many of the existing animations
as a first step toward implementing the new ones.

https://bugzilla.gnome.org/show_bug.cgi?id=694062
2013-02-19 18:39:53 -05:00
f8446f1bfc loginDialog: get rid of title label
the login screen currently says "Sign In" at the top of it.

The latest mock ups don't have that.

This commit drops it.

https://bugzilla.gnome.org/show_bug.cgi?id=694062
2013-02-19 18:39:53 -05:00
b922035d4f iconGrid: Dynamically adjust spacing based on columnLimit
Adjust the spacing based on the available width when a columnLimit
is set to evenly place the icons.

https://bugzilla.gnome.org/show_bug.cgi?id=694215
2013-02-20 00:39:18 +01:00
b75fc2bde3 appDisplay: Add additional view showing frequently used apps
According to the design mockups, the app picker should follow the
recent view pattern as used by applications, where the user is first
offered a subset of applications he/she is likely to start, and only
then allow switching to the full set of installed applications.
So implement the ability to manage several views in AppDisplay and add
FrequentView as additional view, which uses the existing ShellAppUsage
to display a list of frequently used applications.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:18:56 +01:00
10e16cca22 shell-app-usage: Remove unused parameter
https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:18:56 +01:00
6628214484 appDisplay: Make folder popups "view-modal"
While a group is open, we want to block events on items underneath,
but still allow interaction with components outside the app display
as well as scrolling of the view as a whole.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:18:56 +01:00
e3c9c46c1c appDisplay: Create AppFolderIcons for selected categories
App folders are intended for grouping some applications, not to
assign a category to every single application, so we will only
create folders for a selected subset of the existing categories.
Software/Alacarte will eventually allow to create/modify those
folders, so store the setting in GSettings so that it can be
shared with those applications.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:18:56 +01:00
6a1b341336 appDisplay: Add AllView.addFolder() method
Adjust AllView to be able to hold both apps and folders, and add an
addFolder() method to insert a folder.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:18:56 +01:00
65f96494f8 appDisplay: Separate app loading from filling the grid
At the moment when loading the applications, each app is inserted
at its correct (alphabetical) position. Avoid this overhead by
loading all apps first, then sort them once and fill the grid with
the sorted actors.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:18:56 +01:00
6e3e2d9f29 appDisplay: Add new FolderIcon class
FolderIcons will appear in the primary app view along AppIcons, but
represent a group of applications rather than a single application.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:18:56 +01:00
25d8debb11 appDisplay: Add intermediate stack widget to hold folder popups
AllView will be updated to allow app folders in addition to app
launchers. Folders will pop up in the existing view, so add an
additional stack widget into the hierarchy which popups will use
as parent.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:18:56 +01:00
426b227f03 appDisplay: Make AlphabeticalView an abstract base class of AllView
We are going to introduce app folders, which will also present an
alphabetical list of applications, but use a different UI. In
preparation for that, split out the item logic into an abstract
base class.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:18:56 +01:00
2fbd9b95a7 appDisplay: Set a maximum column number
In particular on widescreen monitors, the number of items in a row
can be overwhelming, so limit the number of columns and center the
view.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:08:11 +01:00
9758029c11 appDisplay: Make _loadCategory() a utility function
When we bring back categories as folders, this method will be
useful outside of AppDisplay.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:08:11 +01:00
f21b8206af messageTray: Move makeCloseButton() into Util
The function is useful for other modules as well, so move it to a
more generic place.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:08:11 +01:00
4f8f79a907 appDisplay: Remove unused variables
https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:08:11 +01:00
c9e2f16404 appDisplay: Merge AllAppDisplay and ViewByCategories
With categories removed, the separation between AllAppDisplay and
ViewByCategories no longer makes sense. Also use this opportunity
to rename the outdated AllAppDisplay to AppDisplay; it will
eventually be used to manage different views.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:08:11 +01:00
2658754295 appDisplay: Merge AppIcon and AppWellIcon
AppIcon is just a tiny wrapper around BaseIcon, which does not add
anything over using BaseIcon directly, so merge its code with
AppWellIcon. As the concept of the "app well" has not been used
since well before 3.0, use AppIcon as name for the merged class.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:08:11 +01:00
77d8ff3620 appDisplay: Remove categories
We will eventually display some categories as app folders, but the
existing code doesn't help with the implementation, so remove it.

https://bugzilla.gnome.org/show_bug.cgi?id=694192
2013-02-20 00:08:11 +01:00
c4621119b3 AccessibilityMenu: hide when not needed
Tie the menu visibility to having at least one a11y feature enabled.

https://bugzilla.gnome.org/show_bug.cgi?id=681528
2013-02-19 22:07:08 +01:00
a2a292d48e Updated POTFILES.in 2013-02-19 20:29:16 +01:00
8349b70ba8 theme: add some transparency to the menu backgrounds
They look too hard with a fully opaque black background. A small
bit of transparency will give a softer appearance without impairing
readability.

https://bugzilla.gnome.org/show_bug.cgi?id=694202
2013-02-19 18:55:09 +00:00
dd6fe58475 polKitAgent: Don't crash when we cannot enable/disable the auth agent
This makes it easier to run with jhbuild.

https://bugzilla.gnome.org/show_bug.cgi?id=687556
2013-02-19 17:25:53 +01:00
386929e84b l10n: Updated Italian translation 2013-02-19 11:07:46 +01:00
43a355e07a userMenu: Use single quotes for non translateable strings
Coding style cleanup ...
2013-02-19 08:44:01 +01:00
1a5d2ac459 build: Bump gnome-desktop requirement to 3.7.90 2013-02-19 07:54:52 +01:00
21403b19e0 overviewControls: use Params for SlidingControl constructor
https://bugzilla.gnome.org/show_bug.cgi?id=686984
2013-02-18 22:50:11 -05:00
cb7778d433 dash: don't react to window drags
Instead, make the actor half-opaque in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=686984
2013-02-18 22:50:11 -05:00
07c0105c83 loginDialog: don't try to stop work spinner if it's already gone
This can happen if the dialog gets reset at the wrong moment.

https://bugzilla.gnome.org/show_bug.cgi?id=693757
2013-02-18 20:35:05 -05:00
7da186d4e9 loginDialog: fix cancel from "Not Listed?"
Since commit 1ae0fadbf4 we
no longer start a PAM conversation as soon as the user clicks
the "Not Listed?" button.  Instead we defer starting the PAM
conversation until the user types their username.

Unfortunately, the cancel button resets the dialog back to the
user list indrectly by cancelling the current PAM conversation.

This means if the user hasn't yet entered thier username then
the cancel button doesn't work.

This commit performs a direct dialog reset in the case the
PAM conversation hasn't been started yet.

https://bugzilla.gnome.org/show_bug.cgi?id=693756
2013-02-18 20:34:20 -05:00
75885c3095 appDisplay: Fix _ensureIconVisible()
The fade effect was renamed a while ago, but we missed to update
the use in _ensureIconVisible().

https://bugzilla.gnome.org/show_bug.cgi?id=694125
2013-02-18 22:44:23 +01:00
5d275389af scroll-view-fade: Fix [vh]fade_offset property getters
The two were mixed up all along, and nobody noticed ...

https://bugzilla.gnome.org/show_bug.cgi?id=694125
2013-02-18 22:44:23 +01:00
75589b4450 shellEntry: Don't navigate forward before popping up the menu
Doing it before the grab messes up the grab helper's tracking
of where the focus was before, leading to it not tracking the
saved focus correctly, meaning that when we pop the menu back
down with escape, it doesn't restore focus correctly.

https://bugzilla.gnome.org/show_bug.cgi?id=694123
2013-02-18 16:17:09 -05:00
a9b12d5d73 shellEntry: Add an animation when opening the menu 2013-02-18 16:02:44 -05:00
6a00a504d4 backgroundMenu: Don't add an animation to the background menu
We don't do this for shell entries nor for the message tray
context menu, so it doesn't make sense to do it here, either.
2013-02-18 16:01:02 -05:00
6b9cac928b shellEntry: Add a modeline 2013-02-18 16:01:02 -05:00
b292384380 messageTray: Animate the context menu
Use animation for opening and closing the menu.
2013-02-18 22:00:39 +01:00
01ac5aeb0d messageTray: Use 'Clear Message' instead of 'Clear for the context menu 2013-02-18 21:59:48 +01:00
1a41ee0c30 backgroundMenu: Fix a string
The proper terminology is "Background". And while we're at it,
use a real ellipsis.
2013-02-18 15:48:30 -05:00
1950a67e15 Run the startup animation when we don't have much going on
Starting the startup animation when we don't have that much IO
makes it a lot more visible.

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

https://bugzilla.gnome.org/show_bug.cgi?id=682429
2013-02-18 15:37:59 -05:00
65303d027a main: defer startup until we know our session mode
commit 92083eaf76 made
session mode loading an asynchronous operation.

Aspects of the session mode aren't known immediately at
start up. For instance, sessionMode.isGreeter returns
false for greeter sessions until the asynchronous
operation completes.

This commit defers start up processing until the session
mode is fully known.

https://bugzilla.gnome.org/show_bug.cgi?id=682429
2013-02-18 15:37:44 -05:00
fae838b054 backgroundMenu: Add a context menu on the background actor
Allow users to change their wallpaper and launch System Settings
from it.

https://bugzilla.gnome.org/show_bug.cgi?id=681540
2013-02-18 15:23:25 -05:00
eab4c7bce9 Don't call beginItemDrag() for XDND
The dragged actors are not overview items.

https://bugzilla.gnome.org/show_bug.cgi?id=686984
2013-02-18 14:54:53 -05:00
6bb38b873b appDisplay: remove unused parameter to AppWellIcon constructor
https://bugzilla.gnome.org/show_bug.cgi?id=686984
2013-02-18 14:54:53 -05:00
ba5860cdd6 xdndHandler: Add the dummy actor to the uiGroup
While this isn't too urgent, since this won't break if we don't have
a drag actor, I was wondering what the other child in my stage was.
2013-02-18 14:47:17 -05:00
ee62863759 messageTray: Don't allow opening while entering/leaving the overview
The complex overview transition code means that there's no easy
way to handle with this right now. Blocking the message tray
while the overview is animating seems like the correct thing to do.

https://bugzilla.gnome.org/show_bug.cgi?id=694038
2013-02-18 14:42:44 -05:00
4a3a61bea1 messageTray: Jiggle some code around
This is to put state management with state management

https://bugzilla.gnome.org/show_bug.cgi?id=694038
2013-02-18 14:42:44 -05:00
f3901eb668 messageTray: Drop more special overview handling
https://bugzilla.gnome.org/show_bug.cgi?id=694038
2013-02-18 14:42:44 -05:00
b53d1df4df messageTray: Re-add the hotcorner when the panel is updated
The activities button may come and go at any moment now that we
have a dynamic panel. We need to re-check the activities button
whenever the panel is updated.

https://bugzilla.gnome.org/show_bug.cgi?id=694038
2013-02-18 14:42:44 -05:00
df9b0c548e theme: Clean up and merge window-caption definitions 2013-02-18 14:24:40 -05:00
3056fc4710 overviewControls: don't perform translation when target is met already
Fixes a regression from ccb81919f7
2013-02-18 14:18:57 -05:00
e411a4af21 userMenu: Use the session id to detect the current session
This allows us to detect startx sessions.

https://bugzilla.gnome.org/show_bug.cgi?id=693896
2013-02-18 20:13:38 +01:00
3430012136 overview: Don't display minimized windows differently in the overview
Making them not fully opaque just makes them harder to see and there is no reason why the user should care whether the window is minimized or not when
switching to a window display them like any other windows.

https://bugzilla.gnome.org/show_bug.cgi?id=693991
2013-02-18 19:40:48 +01:00
809295c03d popupMenu: Close all child menus when the main menu is closed
Otherwise, if something managed to call close(), it wouldn't
close any child menus.

https://bugzilla.gnome.org/show_bug.cgi?id=693572
2013-02-18 13:31:15 -05:00
c9783b38c4 popupMenu: Move child menus to be a PopupMenu-only thing
It doesn't make sense for combo menus, sections, or submenus
to have child menus.

https://bugzilla.gnome.org/show_bug.cgi?id=693572
2013-02-18 13:31:15 -05:00
44e4cbf04a popupMenu: Remove support for nested combo boxes
I'm sure this was originally a testcase for nested child
menus, and we never actually inteded any support for this.

https://bugzilla.gnome.org/show_bug.cgi?id=693572
2013-02-18 13:31:15 -05:00
049695b914 Minimize fullscreen windows when they end up in the background
Alt-Tab away from a monitor sized on the primary monitor results into the top
panel being displayed on top of the window which looks very bad.

So just hide those windows by minimizing them.

The icon geometry animation does not really make sense for fullscreen windows
so just fade them out.

https://bugzilla.gnome.org/show_bug.cgi?id=693991
2013-02-18 19:30:56 +01:00
7b1aab3759 layout: Fix tray barrier never getting destroyed
Due to a typo, we accidentally set trayPressure to null,
making the next branch always fail.

https://bugzilla.gnome.org/show_bug.cgi?id=694070
2013-02-18 13:25:12 -05:00
5e87fea1ee StEntry: reset the cursor when unmapped
We cannot reset the cursor at the next leave event, as that might
happen on a NULL stage and cause a BadWindow error, so do it on
unmap (which is guaranteed to happen before the stage is cleared).

https://bugzilla.gnome.org/show_bug.cgi?id=694057
2013-02-18 18:08:57 +01:00
a187111a26 OverviewControl: simplify code to add bottom padding
All the complexity with a custom actor and a generic container was
just to add some padding below the overview controls. Remove that,
and use CSS instead.

https://bugzilla.gnome.org/show_bug.cgi?id=694100
2013-02-18 17:37:46 +01:00
3044a6b517 Remove builtin settings search provider
It is now provided as a remote provider by gnome-control-center.

https://bugzilla.gnome.org/show_bug.cgi?id=690824
2013-02-18 16:56:45 +01:00
824fbe09c2 keyboard: track XKB group configuration change
Since GNOME 3.6, switching XKB layouts changes the group
configuration.  This patch tries to track group configuration changes
and reconstruct UI as needed.  See also caribou bug#694011.

https://bugzilla.gnome.org/show_bug.cgi?id=681735
2013-02-19 00:32:26 +09:00
764eed7599 l10n: Updated Italian translation 2013-02-18 15:39:01 +01:00
ccb81919f7 overviewControls: update slide in translations from pageEmpty
Now that we unified animation times, move in controls together with the
new view.

https://bugzilla.gnome.org/show_bug.cgi?id=694064
2013-02-18 09:34:06 -05:00
4505c38b58 overviewControls: fix math for translations when sliding in
The math to compute translations was wrong when sliding in; also, don't
depend on the slide value for the translation anymore.

https://bugzilla.gnome.org/show_bug.cgi?id=694064
2013-02-18 09:34:06 -05:00
d9770dcffb layout: Fix regressions
Commit 6b4f524620 removed the layer checks
_updateFullscreen ... this causes corruption when alt-tabbing out
of a fullscreen window so restore the check.

The commit also removed the screen sized check so we are no longer
setting all monitors to fullscreen. Fix that as well by using
window.is_screen_sized() to perform the check.

https://bugzilla.gnome.org/show_bug.cgi?id=694079
2013-02-18 14:13:37 +01:00
9936f97fcb overview: Sort windows by travel distance within rows
Sort windows inside the rows by there travel distance.

https://bugzilla.gnome.org/show_bug.cgi?id=690313
2013-02-18 12:02:22 +01:00
4cf09f46e7 messageTray: Fix indentation 2013-02-18 04:37:21 -05:00
6ffe3a424c grabHelper: Fix indentation
https://bugzilla.gnome.org/show_bug.cgi?id=694038
2013-02-18 04:31:45 -05:00
641794d458 layout: Reword a comment
I don't even know what this was trying to say before...
2013-02-18 04:31:45 -05:00
73d9ac6c01 overviewControls: synchronize viewSelector fade and controls slide
If the animation time is not the same for these two, the translation
will be adjusted to the allocation during the tween, resulting in a jump
in the animation.

https://bugzilla.gnome.org/show_bug.cgi?id=694035
2013-02-18 00:30:04 -05:00
3205d1e16a windowManager: Fix fallback path for icon_geometry animation
It doesn't seem like this code was ever tested.

https://bugzilla.gnome.org/show_bug.cgi?id=694052
2013-02-17 18:57:42 -05:00
ec61004622 Overview: clip contents to the allocation
If they're not clipped, overview controls can leak to secondary monitors
while animating.
2013-02-17 22:38:40 +01:00
ae93d258a5 layout: Call meta_window_* on the meta_window rather then on the actor 2013-02-17 21:50:23 +01:00
12845f0eef overviewControls: reset any translation when sliding in for drag
Fixes the dash not sliding when dragging an app from the search results.
2013-02-17 15:48:25 -05:00
6b4f524620 layout: Use window.is_monitor_sized
Mutter now provides a method for this, so use it.
2013-02-17 21:43:11 +01:00
dec0baa147 Remove gap between windows when switching workspaces
Previously there was a distracting gap between workspaces with two full-screen
applications.

https://bugzilla.gnome.org/show_bug.cgi?id=685849
2013-02-17 21:05:55 +01:00
340609d149 Fix XDND regression
The top_window_group blocks the panel elements from being found by the XDND
pick so hide the whole group from picks as we never attempt to pick its contents
anyway.
2013-02-17 20:52:00 +01:00
9a30c3d722 messageTray: Allow dwell to work in the overview
Special case the overview in _trayDwellTimeout because the
tray is hidden by default in the overview now.
2013-02-17 20:16:52 +01:00
e19a927579 overviewControls: Fix reallocation cycles
Instead of setting a new height, let the Clutter relayout
machinery do the work for us.
2013-02-17 13:53:24 -05:00
53a595885a pressure-barrier: don't discard grabbed events when overview is grabbing
Since we still want to trigger the message tray in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=693987
2013-02-17 13:12:55 -05:00
ebd7406386 main: remove unused function
https://bugzilla.gnome.org/show_bug.cgi?id=693987
2013-02-17 13:12:55 -05:00
66cdbc5cad overviewControls: account for the search entry in each control
Account for the search entry space at the bottom (the former message
tray clone) individually in each side control, instead of packing
another actor in the overview.
This allows us to extend the central view all the way to the bottom,
while still keeping controls centered vertically.

https://bugzilla.gnome.org/show_bug.cgi?id=693987
2013-02-17 13:12:55 -05:00
060c049056 messageTray: remove overview special handling
Don't show the message tray in the overview by default. From now on the
message tray in overview behaves as regularly, i.e. it will slide up the
overview on Super+M keypress.

https://bugzilla.gnome.org/show_bug.cgi?id=693987
2013-02-17 13:12:54 -05:00
31270c36d2 Updated Polish translation 2013-02-17 18:36:20 +01:00
0aa26e9134 Calendar: don't initialize the event source synchronously
We don't want to block the shell start to load evolution-data-server
and open all calendars (which can involve network connections)

https://bugzilla.gnome.org/show_bug.cgi?id=694030
2013-02-17 16:06:00 +01:00
b45a2d7335 ScreenShield: don't add the source actor directly to the boxlayout
StBoxLayout always fills on the orthogonal direction, so the icon
becomes distorted as the layout grows to accomodate more details.
Instead, use a bin that aligns at the start.

https://bugzilla.gnome.org/show_bug.cgi?id=693822
2013-02-17 14:53:41 +01:00
07676d483d MessageTray: don't clear the banner text when adding to the body
ScreenShield uses Notification.bannerBodyText to fill the body of detailed
notifications, so use a separate boolean property to indicate it was already
added to the body.

https://bugzilla.gnome.org/show_bug.cgi?id=693822
2013-02-17 14:53:41 +01:00
6436452bc9 Updated Galician translations 2013-02-17 13:51:33 +01:00
60ad374514 Updated Brazilian Portuguese translation 2013-02-16 18:10:09 -02:00
b611ddda5e messageTray: Close menu when clicking outside of the menu 2013-02-16 19:55:59 +01:00
186bd156dd messageTray: Allow opening the context menu on long press
Right click does not work for touch devices, so support opening the menu
using long press as well.

https://bugzilla.gnome.org/show_bug.cgi?id=693887
2013-02-16 19:37:26 +01:00
3628c81885 grabHelper: Ignore key focus changes when ungrabbing
Calling onUngrab() may change key focus, either directly or
indirectly (e.g. hiding the actor). Such key focus changes
would cause an extra actor to be ungrabbed, so make sure to
ignore such focus changes while we're ungrabbing.

https://bugzilla.gnome.org/show_bug.cgi?id=693975
2013-02-16 13:33:45 -05:00
180000a531 grabHelper: Track the grab before trying to set key focus
If we don't this for a nested grabFocus grab, the notify::key-focus
will be called, not think that the new key focus is part of the
grab, and cancel the full grab. This leaves the grab helper in an
inconsistent and confused state, as the grab is pushed onto the
grab stack after.

https://bugzilla.gnome.org/show_bug.cgi?id=693975
2013-02-16 13:33:45 -05:00
5f995c64d4 grabHelper: Correct typo preventing focus-window-changed disconnect
While debugging, I found that the signal to focus-window-changed
was never getting disconnected, making a call to ungrab every time
the focus window changed, even if there were no focus grabs anymore.

https://bugzilla.gnome.org/show_bug.cgi?id=693975
2013-02-16 13:33:45 -05:00
fe2c2014de grabHelper: Use a mode: js header
This brings us into consistency with the rest of the modelines

https://bugzilla.gnome.org/show_bug.cgi?id=693975
2013-02-16 13:33:45 -05:00
754df5ebe2 shell-app-system: Ensure that we correctly order subsearches
As we use g_slist_prepend for efficiency when building the list
of results before ordering it, we need to make sure we traverse
the list of previous results backwards so that it's built in
the same order.

https://bugzilla.gnome.org/show_bug.cgi?id=693936
2013-02-16 13:33:45 -05:00
d8b7ac9044 viewSelector: block show apps button checked signal when resetting state
When we reset the state of the checked button due to the overview
showing and hiding, block the normal checked callback and immediately
switch to the workspaces page, so that windows seamlessly fade in.

https://bugzilla.gnome.org/show_bug.cgi?id=693974
2013-02-16 13:26:38 -05:00
5f3b04ecbd viewSelector: make the old page fade-out optional
When leaving the overview, we don't want the old page to fade out, but
we want to fade in the windows directly.

https://bugzilla.gnome.org/show_bug.cgi?id=693974
2013-02-16 13:26:38 -05:00
1f1aba4a32 viewSelector: always reset search string when entering overview
Instead of relying on the apps button callback to do it for us.

https://bugzilla.gnome.org/show_bug.cgi?id=693974
2013-02-16 13:26:38 -05:00
1390cd4048 viewSelector: don't use resetShowAppsButton when pressing escape
Set the checked property explicitly. This is because
resetShowAppsButton() will only be used to flip the button state
blocking the page change in a future commit.

https://bugzilla.gnome.org/show_bug.cgi?id=693974
2013-02-16 13:26:38 -05:00
31cdac7bfb overview: change _hideInProgress to visibleTarget
And use it in overviewControls. When we moved this code from overview.js
to overviewControls.js we lost a condition so we now slide in controls
even when going back from the overview, which looks bad.

https://bugzilla.gnome.org/show_bug.cgi?id=693974
2013-02-16 13:26:38 -05:00
0705901bce overviewControls: reset translation as well when entering overview
https://bugzilla.gnome.org/show_bug.cgi?id=693974
2013-02-16 13:26:38 -05:00
87495575bc search: Don't run a full search for equal terms
Due to the timeout on the search entry, it's possible for
a very quick edit to the entry to launch a subsearch on the
same search string.

https://bugzilla.gnome.org/show_bug.cgi?id=693937
2013-02-16 13:24:21 -05:00
b292bba092 search: Simplify and correct behavior for substring searches
Early on, search was based on a list of terms, which was like a set
of tags, in that terms were OR'd, and that order didn't matter. As
such, modifying any one of the terms wouldn't produce new results.
Nowadays, providers take the order into account, so a substring
should only be the case if new terms are added to the end.

https://bugzilla.gnome.org/show_bug.cgi?id=693935
2013-02-16 13:24:21 -05:00
5c7f0a0ad8 WindowOverlay: remove animations when relayouting
If WindowOverlay.relayout() is called without animation, we must stop
any preexisting animation, otherwise it will continue to run with the
previous parameters and cause the overlay to end up in the wrong position.

https://bugzilla.gnome.org/show_bug.cgi?id=693970
2013-02-16 19:08:02 +01:00
2df86e1966 shell-keybinding-modes: Don't use expression references
This confuses the gobject-introspection scanner, silently
making SHELL_KEYBINDING_MODE_NONE into -1.

https://bugzilla.gnome.org/show_bug.cgi?id=693940
2013-02-16 11:44:47 -05:00
41406e3be2 overviewControls: use translations for explicit slide in/out
The slideX property controls the allocation of the view selector; since
we now know when there are no visible views from the page-empty signal,
we can use it to set the full slideX for the next page at that time,
allowing the new view to fade in with the right width.
This allows us to use simple x translations for the side components when
switching pages, keeping the noise due to resizes at the minimum.

The slideX resize for now is kept for DnD, and will always be needed for
the thumbnails box when showing the windows page.

https://bugzilla.gnome.org/show_bug.cgi?id=693924
2013-02-15 23:47:13 -05:00
7ed4bd67b8 overviewControls: split out an util function
Will be used later

https://bugzilla.gnome.org/show_bug.cgi?id=693924
2013-02-15 23:47:12 -05:00
8ce3531de8 overviewControls: make dash private
https://bugzilla.gnome.org/show_bug.cgi?id=693924
2013-02-15 23:47:12 -05:00
579e53f02c overview: move controls visibility handling to a separate object
This keeps the core code of the overview clean and will help
coordinate animations.

https://bugzilla.gnome.org/show_bug.cgi?id=693924
2013-02-15 23:47:12 -05:00
1379c6b404 overview: move ctrlAltTab handling to Dash
This is also what the panel does.

https://bugzilla.gnome.org/show_bug.cgi?id=693924
2013-02-15 23:47:12 -05:00
153d3045ed viewSelector: emit a signal when the view is empty
This is very a useful point in time when the size of the side controls
can change without affecting any visible view. Emit a signal at that
time.

https://bugzilla.gnome.org/show_bug.cgi?id=693924
2013-02-15 23:47:12 -05:00
278686dc48 viewSelector: don't crossfade between pages
Instead, wait until the first animation is completed before switching to
the second page.

https://bugzilla.gnome.org/show_bug.cgi?id=693924
2013-02-15 23:47:12 -05:00
9b9f33bc8b Use the right conversion from cairo to GdkPixbuf
GdkPixbuf is stored in memory as individual bytes, in strict red-green-blue-alpha
order, while cairo image surfaces are stored as 32bits units whose
order depends on the endianess.
It is probably possible to do something better, taking advantage
of cogl and the GL using the actual component order, but for now
it is easier to use the GDK utility to convert the cairo surface.

https://bugzilla.gnome.org/show_bug.cgi?id=693931
2013-02-16 04:09:21 +01:00
24984458de networkAgent: Don't register/unregister, just refuse to handle requests
https://bugzilla.gnome.org/show_bug.cgi?id=693746
2013-02-15 16:50:12 -05:00
e3eb4a20a5 layout: Remove _addBarrierEvent
Moving it inline to when we get the event means that we can
remove the duplicate call to _getDistanceAcrossBarrier.

https://bugzilla.gnome.org/show_bug.cgi?id=693854
2013-02-15 16:49:46 -05:00
001bbd36f5 layout: Fix old event removal
If we increment our index variable while looping, this means that
firstNewEvent will be one higher than it should. With a length 1
array, all events will be removed, so this has a cascading effect
that events will not be stored at all.

https://bugzilla.gnome.org/show_bug.cgi?id=693854
2013-02-15 16:49:46 -05:00
382cc52baa NetworkMenu: use async initialization for libnm-glib objects
NMClient recently got more heavyweight, with a property holding supported
connections. As fully initializing a NMObject is a recursive operation
and requires multiple DBus calls, switch to async initalization for NMClient
and NMRemoteSettings.

https://bugzilla.gnome.org/show_bug.cgi?id=683288
2013-02-15 21:37:04 +01:00
2af368ab82 Updated Lithuanian translation 2013-02-15 21:49:34 +02:00
f514160fae unlockDialog: fix another occurrence of add_watch => add_idle_watch
https://bugzilla.gnome.org/show_bug.cgi?id=693909
2013-02-15 12:41:17 -05:00
30e4dcef90 keyboard: Don't show the keyboard when enabled
https://bugzilla.gnome.org/show_bug.cgi?id=692773
2013-02-15 18:23:55 +01:00
4618e11406 screenShield: Fix crash with the idle monitor 2013-02-15 08:01:00 -05:00
f887b77e36 Updated Serbian translation 2013-02-15 12:25:30 +01:00
fb527139a0 build: Bump gtk+ requirement to 3.7.9
Needed by 7fc2b33a0a
2013-02-15 09:41:01 +01:00
261fbef516 Update to new GnomeIdleMonitor API
https://bugzilla.gnome.org/show_bug.cgi?id=688227
2013-02-15 09:13:02 +01:00
32e8a9b377 keyboard: respect "center" alignment of keys
https://bugzilla.gnome.org/show_bug.cgi?id=679925
2013-02-15 12:27:43 +09:00
5f95351074 tweener: follow org.gnome.desktop.interface enable-animations
Disable the animations by making the animation time a very small value.

https://bugzilla.gnome.org/show_bug.cgi?id=655746
2013-02-14 20:42:53 -05:00
1c57c0e651 endSessionDialog: don't use Tweener for the timer
We want to make Tweener short-circuit animations when resources are
constrained, so this is not going to work.
Instead, use a one-second timeout until the seconds left reach zero.

https://bugzilla.gnome.org/show_bug.cgi?id=655746
2013-02-14 20:42:53 -05:00
ec6ee27f29 [l10n] Updated Catalan (Valencian) translation 2013-02-15 01:13:55 +01:00
70234edd02 [l10n] Updated Catalan translation 2013-02-15 01:13:49 +01:00
7e24a696bd ibusCandidatePopup: Fix cursor positioning
After moving the dummy source actor, we still have to poke the
boxpointer so that it gets repositioned.

This has always been broken but went unnoticed until now since none of
the commonly used engines currently depend on this method. Thanks to
Mathieu Bridon for pointing it out.

https://bugzilla.gnome.org/show_bug.cgi?id=691902
2013-02-15 00:26:47 +01:00
c3ed936776 ibusCandidatePopup: Make candidates reactive to pointer clicks
Allow for candidates to be selected by clicking on them.

https://bugzilla.gnome.org/show_bug.cgi?id=691902
2013-02-15 00:26:47 +01:00
235ec7cb2e CandidateArea: make setOrientation() public
setCandidates() has too many arguments and setting the orientation
isn't particularly related with it. It might also be useful to switch
orientation without changing the candidates.

https://bugzilla.gnome.org/show_bug.cgi?id=691902
2013-02-15 00:26:47 +01:00
4f8586d81d ibusCandidatePopup: Add pagination buttons
Makes switching candidates pages with pointer clicks possible.

https://bugzilla.gnome.org/show_bug.cgi?id=691902
2013-02-15 00:26:47 +01:00
6cd1e38425 ibusCandidatePopup: Style updates according to design mockups
Make it look more like the mockups.

In order to do that we stop using PopupMenu and friends as it doesn't
really buy us anything and just makes it more cumbersome to add the
style classes we need.

https://bugzilla.gnome.org/show_bug.cgi?id=691902
2013-02-15 00:26:47 +01:00
f7512dc540 viewSelector: don't change page on drag begin
We now slide in controls as appropriate when a drag is started.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2013-02-14 18:18:22 -05:00
45415a742f overview: hide side controls when searching
Hide the elements when the search is active. Show them if the search
is cancelled.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2013-02-14 18:18:22 -05:00
2247065dc5 dash: add a SlidingControl for the dash actor
Will be needed to hide and show it when searching.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2013-02-14 18:18:22 -05:00
c550e2ccf5 workspacesView: simplify code
We always pass the same coordinates to setGeometry and setClipRect in
WorkspacesView; remove the latter to simplify code.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2013-02-14 18:18:22 -05:00
bc75e5ec44 workspace: include the close button overlap in the right side chrome
As we do for the top side; the border is overlaied in the top/right
corner.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2013-02-14 18:18:22 -05:00
5b39496008 overview: set side controls visibility on view page change
For now use the same behavior as before - hide the workspace thumbnails
when showing the applications page.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2013-02-14 18:18:21 -05:00
4016da6632 viewSelector: add a method to get the currently active page
https://bugzilla.gnome.org/show_bug.cgi?id=682050
2013-02-14 18:18:21 -05:00
f2edcb9bdf viewSelector: notify on active page changes
In order to do this, we also need to move the assignment of
this._activePage from the animation onComplete callback to the function
itself.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2013-02-14 18:18:21 -05:00
3d8a87563d workspaceThumbnail: pack ThumbnailsBox in the overview directly
Use a SlidingControl subclass and pack that in the overview group
directly.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2013-02-14 18:18:21 -05:00
93bde0cae2 Introduce a SlideLayout layout manager
SlideLayout is a fixed layout that takes care of requesting and
allocating the right sizes so its contents can slide horizontally as the
actor is resized.
Sliding is controlled with a slideX and slideDirection properties, which
do the right thing wrt. RTL automatically.

Also add a SlidingControl base class that will be used by the overview
to pack and slide the workspace thumbnail switcher and the dash.

https://bugzilla.gnome.org/show_bug.cgi?id=682050
2013-02-14 18:18:21 -05:00
30179bb60d popupMenu: Treat a menu will all invisible menu items as empty
As an example, a menu that has only settings actions might
be "empty" if allowSettings is false.

https://bugzilla.gnome.org/show_bug.cgi?id=681540
2013-02-14 18:16:21 -05:00
498bc21133 theme: Fix style
https://bugzilla.gnome.org/show_bug.cgi?id=681540
2013-02-14 18:16:21 -05:00
af7a34521f ShellSearchProvider: Update documentation about 'name' and 'id'
Document and enforce that a meta ID corresponds to the result ID
that we've passed in. This does not break any existing search providers
as far as I'm aware.

https://bugzilla.gnome.org/show_bug.cgi?id=693836
2013-02-14 16:44:14 -05:00
7fc2b33a0a st-texture-cache: Use async variants of the icon loading API
While we were relying on gtk_icon_info_load_icon and friends being
thread-safe, there was no such guarantee, and recent caching that
was added to GTK+ made it non-threadsafe. To replace it, _async()
variants of the icon loading code were added that are thread-safe.
Use those instead of using our own worker threads.

https://bugzilla.gnome.org/show_bug.cgi?id=692845
2013-02-14 16:16:00 -05:00
a5d3f7785a st-texture-cache: Add one simple way to free texture load data
https://bugzilla.gnome.org/show_bug.cgi?id=692845
2013-02-14 16:09:04 -05:00
f1cdce38a6 st-texture-cache: Remove a no-op scale pixbuf
We already passed GtkIconTheme the size when it wanted to load it,
so it knows the desired size and will scale it to what we want.

https://bugzilla.gnome.org/show_bug.cgi?id=692845
2013-02-14 16:09:04 -05:00
65e4652142 viewSelector: Make sure not to start searching when we only have whitespace
As this is thrown out before we actually query providers, we end up with
a confusing screen that says "Searching..." that won't ever get results.

https://bugzilla.gnome.org/show_bug.cgi?id=693458
2013-02-14 14:13:50 -05:00
569797d7c5 searchDisplay: Remove doSearch
This is nothing but a middle man, as the view selector already owns
the search system. We want to start being a bit more tricky with what
we do with the search system so that we ignore whitespace, so let's
cut the middle-man out now.

https://bugzilla.gnome.org/show_bug.cgi?id=693458
2013-02-14 14:05:37 -05:00
35a7c94cd1 search: Fix style
https://bugzilla.gnome.org/show_bug.cgi?id=693458
2013-02-14 14:05:26 -05:00
c8365b7444 viewSelector: Don't strip whitespace from search strings
This is already done by the search system itself.

https://bugzilla.gnome.org/show_bug.cgi?id=693458
2013-02-14 14:04:00 -05:00
8c5a343729 accessibility: Remove some unused constants 2013-02-14 13:45:40 -05:00
22ddec46ab Place popup menus and other override-redirect windows on top of the panel
https://bugzilla.gnome.org/show_bug.cgi?id=633620
2013-02-14 13:45:39 -05:00
a4e70ba4ca layout: Restructure input region and struts code
Have two branches, one for input region and one for struts. This
makes it easier to skip one of the branches, like in the case where
we want to skip input regions if we have a popup menu visible.

https://bugzilla.gnome.org/show_bug.cgi?id=633620
2013-02-14 13:24:04 -05:00
978ac65cae layout: Don't use reparent
reparent() defines the new actor stacking order based on the
existing depth of the actor, which is flat out wrong. Simply
remove the actor from its old parent and add the new one in.

https://bugzilla.gnome.org/show_bug.cgi?id=633620
2013-02-14 13:24:04 -05:00
eb5a836822 screenshot: Write metadata to png file
Use GdkPixbuf rather then cairo_surface_write_to_png_stream when saving
screenshots because this allows us to embedded metadata into the file which
is used by the background control panel to filter out screenshots.

https://bugzilla.gnome.org/show_bug.cgi?id=693737
2013-02-14 19:21:15 +01:00
ad1b9b71ae grabHelper: Merge _navigateActor() with its only user
https://bugzilla.gnome.org/show_bug.cgi?id=693570
2013-02-14 19:17:32 +01:00
60257f422a grabHelper: Restore the actually saved focus on ungrab
GrabHelper saves the actor that had key focus when taking over the grab
(if any). On ungrab, the key focus is either restored or moved to some
child of the saved actor. The latter is unexpected and causes some odd
behavior, so don't be fancy and only restore the actual focus.

https://bugzilla.gnome.org/show_bug.cgi?id=693570
2013-02-14 19:17:32 +01:00
6576d4852c Updated Malayalam Localization 2013-02-14 22:17:03 +04:00
c30661c44c NotificationDaemon: support sound in notifications
The notifications spec has two hints for playing a sound, sound-file
and sound-name. We can support them using the existing code that
wraps libcanberra.

https://bugzilla.gnome.org/show_bug.cgi?id=642831
2013-02-14 18:54:08 +01:00
2d9cf195d7 layout: Remove obsolete message tray <=> OSK interaction code
Message tray and on-screen keyboard are now exclusive, so remove
all code that shuffles boxes around to make it possible to show
both at the same time.

https://bugzilla.gnome.org/show_bug.cgi?id=662687
2013-02-14 17:31:32 +01:00
a21e76caab keyboard: Remove 'tray' button
Now that message tray and keyboard are never shown together, the
odd 'tray' button is pointless, kill it.

https://bugzilla.gnome.org/show_bug.cgi?id=662687
2013-02-14 17:31:32 +01:00
a534a6bf09 messageTray: Always hide while the OSK is up
The message tray currently operates in three modes: in the overview,
normal, and while the on-screen keyboard is up. The last case is
particularly odd, and exclusively used for chat-notifications. As
users can still use the Chat application directly on touch-only
devices, the additional mode isn't really justified, so remove it.

https://bugzilla.gnome.org/show_bug.cgi?id=662687
2013-02-14 17:31:32 +01:00
2b0f219bfd Updated Spanish translation 2013-02-14 16:54:22 +01:00
33dde63256 Main: move the stage hierarchy initialization to LayoutManager
It is cleaner to concentrate all layout and chrome management
in one place, instead of having it scattered around the codebase.

https://bugzilla.gnome.org/show_bug.cgi?id=682429
2013-02-14 01:12:24 -05:00
4dc9540325 ScreenShield: try harder to become modal, and catch failures
The screenshield was not checking the return value of pushModal(), meaning
that it believed it was fully locked when it was not. Later, calling
popModal() would fail, causing an exception and blocking the unlock.
Now when we fail we include an explanatory message, pointing the user
to the actual cause of the issue.

https://bugzilla.gnome.org/show_bug.cgi?id=689106
2013-02-13 17:46:39 +01:00
2b1a661614 ScreenShield: use a longer animation when locking manually
The curtain animation looks jerky at its current speed, and more so if
we blank the screen immediately at the end. Make it a little slower and
it becomes more confortable.

https://bugzilla.gnome.org/show_bug.cgi?id=691964
2013-02-13 15:14:37 +01:00
2138fc2349 messageTray: Make clear item insensetive when there is nothing to clear
There is no point in having a menu entry that does nothing so set the item
to insensitive when there are no items to clear.
2013-02-13 13:12:03 +01:00
cdc8dd88e9 Updated Uyghur translation
Signed-off-by: Gheyret Kenji <gheyret@gmail.com>
2013-02-13 19:59:00 +09:00
2a5f8e84bb js: Use proper Unicode ellipsis (…) instead of three dots
https://bugzilla.gnome.org/show_bug.cgi?id=689542
2013-02-12 17:05:00 -05:00
6f8540f25a Use nice Unicode for date formatting
The ratio character is nicer aligned for this use than the
stock colon, and a thin space is all that is needed before am/pm.

https://bugzilla.gnome.org/show_bug.cgi?id=689251
2013-02-12 10:16:47 -05:00
151c699f5c EndSessionDialog: Add a Close method
When there are multiple sessions, we may get a polkit dialog in
response to clicking 'Reboot' in the end session dialog. If the
polkit dialog gets canceled or otherwise ends unsuccessfully,
we are left with the lightbox that the end session dialog puts
up when 'Reboot' is clicked. To remove the lightbox and make the
session fully functional again, gnome-session will call Close.

https://bugzilla.gnome.org/show_bug.cgi?id=688915
2013-02-12 10:15:51 -05:00
9133f6d352 messageTray: Add context menu
Add a context menu to the tray that allows cleaning up sources
and open the notification settings panel.

https://bugzilla.gnome.org/show_bug.cgi?id=691035
2013-02-12 08:39:21 +01:00
ab276e4e86 693611 fix 2013-02-12 01:00:11 +02:00
cdc4f85e47 shell-screenshot: Fix cursor fetching for non single window screenshots
We have to mark the cairo_surface dirty after modifying the data behind
cairo's back.

Also use CAIRO_FORMAT_ARGB32 rather then CAIRO_FORMAT_RGB24 for the
surface to be consistent with the rest of the code.

https://bugzilla.gnome.org/show_bug.cgi?id=691715
2013-02-11 19:53:54 +01:00
b2fb84e361 Don't ask for a password on shutdown
Show a warning and the list of active users instead when
multiple sessions are present.

https://bugzilla.gnome.org/show_bug.cgi?id=693385
2013-02-11 19:39:00 +01:00
4ab1e893f0 userMenu: Handle the case where the user has no icon file
Fix a crash in UserAvatarWidget.update that gets triggered when the user has
no icon file.

https://bugzilla.gnome.org/show_bug.cgi?id=693385
2013-02-11 19:39:00 +01:00
70bc3d178b Updated Spanish translation 2013-02-11 14:38:12 +01:00
777f145128 st-clipboard: Use GDK for the event filters instead of Clutter
Instead of using Clutter to add an event filter for X events it now
uses the GDK API. The Clutter API won't work if Clutter is not using
an X11-based backend such as if Mutter is directly running with the
KMS backend. This is a step towards making Mutter be its own display
server and a step towards being a Wayland compositor. In this case GDK
will still be using the X backend because it will connect to the
headless X server.

https://bugzilla.gnome.org/show_bug.cgi?id=693438
2013-02-11 09:48:45 +00:00
2f0181ac44 Punjabi: Translation updated (aalam) 2013-02-10 23:38:16 +00:00
83b4b698a0 Updated Malayalam Localization 2013-02-10 18:18:21 +04:00
a10a294a2e st-im-text: Remove unused variable
https://bugzilla.gnome.org/show_bug.cgi?id=693522
2013-02-10 13:39:30 +01:00
34036a3b51 Updated Lithuanian translation 2013-02-09 22:43:42 +02:00
911bc03381 Updated Bulgarian translation 2013-02-09 09:47:25 +02:00
0240aeac8d Updated Uyghur translation
Signed-off-by: Gheyret Kenji <gheyret@gmail.com>
2013-02-09 10:25:34 +09:00
a43be186d3 lookingGlass: Remove old hack for static functions on interfaces
This has been fixed with the addition of interfaces in Gjs.
2013-02-08 14:31:08 -05:00
fb63f48d0a layout: Trigger the message tray by downward pressure
A pressure barrier is a barrier that activates after the user pushes
against the bottom of the screen in a short time. Implement this using
the new XInput 2.3 features that provide extended information about
pointer barriers, and use it so that pushing against the bottom of
the screen edge brings up the message tray.

https://bugzilla.gnome.org/show_bug.cgi?id=677215
2013-02-08 14:28:11 -05:00
c0279df3c6 layout: Port to the new mutter-based barrier wrappers
As pressure barriers need a signalling mechanism to provide
information about when and where they are hit, an object which
provides a signal is a more appropriate abstraction for a pointer
barrier than a functional ID-based approach. Mutter has gained
pointer barrier wrappers, so use its objects instead of ours.

https://bugzilla.gnome.org/show_bug.cgi?id=677215
2013-02-08 14:27:42 -05:00
e614b993f2 messageTray: Remove tray dwelling if we have new barrier features
If we have the new barrier features, then we should not activate the
old tray dwelling mechanism.

https://bugzilla.gnome.org/show_bug.cgi?id=677215
2013-02-08 14:27:42 -05:00
8035f30305 autorunManager: Define this._session as an instance of GnomeSession
This is a fallout from 526a16298c.

https://bugzilla.gnome.org/show_bug.cgi?id=693161
2013-02-08 18:55:38 +01:00
af0b5e6176 searchDisplay: Unset default result when clearing search
If we fail to do so, a previously set result may be activated by
hitting enter, even if a search did not turn up any actual results.
2013-02-08 17:52:39 +01:00
1bf996c705 loginManager: Fix canSuspend in non-logind path
It is supposed to be a NOP, but commit b91d9c2867 got it wrong.

https://bugzilla.gnome.org/show_bug.cgi?id=693162
2013-02-08 17:29:33 +01:00
33f69481f7 Don't rely on implicit type conversion in the scroll view shader
Before version 1.2 of GLSL it would not implicitly convert from int to
float which meant that if you compare a float variable with an integer
constant it will generate a compile error. In particular this means
that on GLES2 (which uses GLSL 1.0) the scroll view shader will not
compile on pedantic compilers, which includes Mesa. This patch just
changes it to use floating point constants.

https://bugzilla.gnome.org/show_bug.cgi?id=693339
2013-02-08 12:03:38 +00:00
354fb6b8c6 Updated Slovenian translation 2013-02-07 20:07:06 +01:00
d30477b6f8 css: Keep the panel height the same in all modes
Since the panel affects struts, if we change its height on mode
transitions, windows will move around which is particularly noticable
with maximized windows when coming out of the screen shield.

https://bugzilla.gnome.org/show_bug.cgi?id=692966
2013-02-07 18:25:36 +01:00
496b5d62ae css: Remove style transitions for the panel
This, together with the panel's transparent background in the screen
shield, has the unfortunate side-effect of showing the desktop
background in a brief flash while coming out of the screen shield.

https://bugzilla.gnome.org/show_bug.cgi?id=692966
2013-02-07 18:25:35 +01:00
3652002a68 screenShield: Pop modes before starting unlock animation
This way the top panel remains visible while the shield hiding
animation is ongoing instead of suddenly appearing just when it ends.

https://bugzilla.gnome.org/show_bug.cgi?id=692966
2013-02-07 18:25:35 +01:00
c0e5719271 keyboard: Don't use set_skip_paint
https://bugzilla.gnome.org/show_bug.cgi?id=693303
2013-02-07 05:43:50 -05:00
997f851031 keyboard: Make sure to set currentSource if we only have one source
This way, popping up and re-closing the switcher won't emit an error
trying to check for this._currentSource.

https://bugzilla.gnome.org/show_bug.cgi?id=693303
2013-02-07 05:43:50 -05:00
eaf184b585 st-entry: Implement middle click paste
https://bugzilla.gnome.org/show_bug.cgi?id=645019
2013-02-07 04:51:30 -05:00
0616261a94 st-clipboard: Add the ability to choose the clipboard type
https://bugzilla.gnome.org/show_bug.cgi?id=645019
2013-02-07 04:51:30 -05:00
d2c45f428f workspace, workspaceThumbnail: Fix another bad rebase
It seems I pushed an old version of the patch by accident.
2013-02-07 02:31:52 -05:00
6054ab35cb workspaceThumbnail: Fix bad rebase 2013-02-07 02:27:40 -05:00
5ba0c6404b main: Move getWindowActorsForWorkspace to layout
It's only used here.

https://bugzilla.gnome.org/show_bug.cgi?id=691746
2013-02-07 01:36:31 -05:00
f738c2be9d main: Remove isWindowActorDisplayedOnWorkspace
We now have a convenience method in mutter.

https://bugzilla.gnome.org/show_bug.cgi?id=691746
2013-02-07 01:36:31 -05:00
12ac2e5534 Switch all external uses of Main.panel.actor.height to the work area
https://bugzilla.gnome.org/show_bug.cgi?id=692680
2013-02-06 22:35:49 -05:00
60985b396a layout: Use the mutter functions for doing monitor math
https://bugzilla.gnome.org/show_bug.cgi?id=692680
2013-02-06 22:35:10 -05:00
d9b33e01ee layout: Replace uses of find(Monitor|Index)ForWindow with window.get_monitor()
Mutter already does the math for us, so we shouldn't have to do it.
2013-02-06 22:35:10 -05:00
0335f2726a workspace: Use mutter's work area calcuating rather than doing it manually
https://bugzilla.gnome.org/show_bug.cgi?id=692680
2013-02-06 22:35:10 -05:00
23717cc4d7 shell: Remove old jsapi-compat file
As we now depend on mozjs185, we know the API we'll be porting to.

https://bugzilla.gnome.org/show_bug.cgi?id=693284
2013-02-06 22:32:03 -05:00
4422f301b4 Updated Dutch translation 2013-02-06 21:53:57 +01:00
1981da1b4e Updated slovak translation 2013-02-06 18:06:32 +01:00
a130f50107 Updated Galician translations 2013-02-06 17:57:56 +01:00
c40198046c Updated slovak translation 2013-02-06 17:39:36 +01:00
e5b44a3970 [l10n] Update Japanese translation 2013-02-06 23:15:57 +09:00
5e7443706b [l10n] remove obsolet message. 2013-02-06 23:08:22 +09:00
39e75e932a [l10n] remove location from ja.po 2013-02-06 23:03:22 +09:00
b8a8edc513 Don't try to use GLX if Cogl isn't using that Winsys
Instead of directly using symbols from GLX to check for the swap event
notification, the plugin now first verifies that the Cogl renderer is
actually using the GLX winsys and then indirectly fetches the pointers
for the GLX functions using cogl_get_proc_address. That way it will
continue to work if Cogl is using an EGL winsys.

Nothing in the Gnome Shell plugin now directly uses symbols from libGL
so we don't need to link to it. This helps to avoid problems linking
against two GL APIs when cogl is using a non-GL driver such as GLES2.

https://bugzilla.gnome.org/show_bug.cgi?id=693225
2013-02-06 00:35:54 +00:00
b1051365fa Bump version to 3.7.5
Update NEWS.
2013-02-06 01:12:32 +01:00
a935cd0c5d build: Update POTFILES.in 2013-02-06 01:06:16 +01:00
62ce90795f userMenu: Remove explicit screen lock on suspend
This is now handled by the screen shield itself.

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

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

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

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

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

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

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

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

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

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

This logic is now exposed by libnm-gtk.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

I wonder when this broke.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

This broke the extension-state-changed signal.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

https://bugzilla.gnome.org/show_bug.cgi?id=687250
2012-12-10 22:34:10 +01:00
13b83c95b7 build: Remove Makefile-gvc.am
This is now unused, and potentially confusing.

https://bugzilla.gnome.org/show_bug.cgi?id=689955
2012-12-10 14:38:07 -05:00
e48dbe65f1 overview: Clean up overview code
The comment here is a liar. We certainly can handle failure
using _syncInputMode().

https://bugzilla.gnome.org/show_bug.cgi?id=688589
2012-12-10 14:38:07 -05:00
14fb51e6d7 overview: Fix stuck grabs when mashing the overlay-key
When pressing the overlay key three times, things went like this:

  * show(), push a modal
  * hide(), will pop a modal after hiding is done
  * show(), push a modal

Thus, when the showing is done, and then it activated the hiding,
it popped one modal, but not the other. This patch changes things
to be:

  * show(), push a modal
  * hide(), will pop a modal after hiding is done
  * hide(), no-op

That is, mashing the overlay-key when it's showing will always make
it hide, not mashing an odd number of times.

https://bugzilla.gnome.org/show_bug.cgi?id=688589
2012-12-10 14:38:07 -05:00
b42af9aa99 popupMenu: Don't slide menus when we're changing them
As calling close() will drop the grab, we were inadverdently
re-closing menus, causing them to re-animate with a full animation.

https://bugzilla.gnome.org/show_bug.cgi?id=689954
2012-12-10 14:38:07 -05:00
ca2ee22827 popupMenu: Display the proper animations for popup menus
BoxPointer's API changed to take a BoxPointer.PopupAnimation
enumeration value, but the code was accidentally replaced with
older code when porting the PopupMenuManager code to GrabHelper.

https://bugzilla.gnome.org/show_bug.cgi?id=689954
2012-12-10 14:38:07 -05:00
13a2b28bf1 popupMenu: Complete the dummy fake menu
"toggle" is used when clicking on the source actor. As we don't
actually have a menu, we don't want to actually take any grabs
through the menu system, so make it a no-op rather than have it
toggle the menu state.

https://bugzilla.gnome.org/show_bug.cgi?id=689954
2012-12-10 14:38:06 -05:00
04bf75d137 panel: Remove a dead check
With the dummy menu item now in place, we will always have a menu.

https://bugzilla.gnome.org/show_bug.cgi?id=689954
2012-12-10 14:38:06 -05:00
76c589702f Assamese translation updated 2012-12-10 18:24:06 +05:30
c11f89e72a messageTray: Don't add ourselves more than once to CtrlAltTabManager
This way _sessionUpdated() can be called multiple times without side
effects.

https://bugzilla.gnome.org/show_bug.cgi?id=689959
2012-12-10 11:50:05 +01:00
cfacb05461 gnome-shell-jhbuild: Allow passing args to gnome-shell
$ ./gnome-shell -- --list-modes

https://bugzilla.gnome.org/show_bug.cgi?id=689778
2012-12-09 18:30:46 -05:00
c4ba31cb23 Updated POTFILES.in
Yes, it has to be there. Run:

git submodule init && git submodule update

to make it work.
2012-12-09 19:13:01 +01:00
db0eacddc5 Updated Norwegian bokmål translation 2012-12-09 14:02:59 +01:00
62972f5138 Update Arabic translation 2012-12-09 04:53:24 +02:00
6fe263f19f Drop removed file 2012-12-09 04:46:09 +02:00
b6a87acb71 gvc: Update git submodule
This fixes the build.
2012-12-08 13:38:43 -05:00
784b04b191 VolumeMenu: show headphone icon when headphones are plugged in
This will show the user where sound will come out, and should
help if he forgets them plugged, or forgets to plug them before
playing music.

https://bugzilla.gnome.org/show_bug.cgi?id=675902
2012-12-08 15:54:05 +01:00
3af9f636af Use libgnome-volume-control from a submodule
To be shared with gnome-control-center and gnome-settings-daemon
2012-12-08 15:53:46 +01:00
16c0585b95 fileUtils: correctly determine file type for symbolic links
Extensions in a folder that is a symbolic link do not get loaded,
since the returned file type is unknown.

https://bugzilla.gnome.org/show_bug.cgi?id=689868
2012-12-08 02:53:21 -05:00
fc9a96ac85 popupMenu: Introduce PopupDummyMenu
This is designed for things like the activities button that sort of
need a menu to make navigation work, but not really have it do anything.

https://bugzilla.gnome.org/show_bug.cgi?id=689109
2012-12-07 19:55:28 -05:00
9dfc3af9d7 popupMenu: Port to GrabHelper
https://bugzilla.gnome.org/show_bug.cgi?id=689109
2012-12-07 19:55:28 -05:00
8dc63932fc grabHelper: Fix up event handling for ignoring releases
We need to return 'true' to signify that we handled the event.

https://bugzilla.gnome.org/show_bug.cgi?id=689109
2012-12-07 19:55:23 -05:00
066e5cddb5 grabHelper: Drop to the actor clicked on
This is necessary for child popups in menus, e.g. while in a combo box,
clicking outside of the user menu should drop the entire menu, but
clicking on the user menu itself should only drop the combo box.

https://bugzilla.gnome.org/show_bug.cgi?id=689109
2012-12-07 19:54:46 -05:00
27ffad2148 grabHelper: Treat the current grabbed actor as a grabbed actor
This should be obvious, but I guess it wasn't necessary for the
message tray case.

https://bugzilla.gnome.org/show_bug.cgi?id=689109
2012-12-07 19:53:47 -05:00
41db363b06 grabHelper: Use captured-event for escape ungrabs
I have no idea why we used 'event' rather than 'captured-event' before.
'event' has some really strange quirks that came up when porting PopupMenu
to the GrabHelper

https://bugzilla.gnome.org/show_bug.cgi?id=689109
2012-12-07 19:53:46 -05:00
184ac11c93 ctrlAltTab: Remove leftover
Accidentally forgot to remove this before pushing.
2012-12-07 13:02:57 -05:00
673aa3c5f2 Updated Kannada Translations 2012-12-07 16:12:16 +05:30
2dd1c04bf3 Updated Kannada Translations 2012-12-07 15:37:39 +05:30
4df1222ce7 screen-shield: disconnect from title-changed on destroy
https://bugzilla.gnome.org/show_bug.cgi?id=689820
2012-12-06 19:07:27 -05:00
208121646c St: recompute all theme nodes when the icon theme changes
StWidget considers "same theme node" as an indication that the style
did not change, and skips emitting style-changed in that case. This
means that icon theme changes are not picked up by StIcon.

https://bugzilla.gnome.org/show_bug.cgi?id=689353
2012-12-06 17:42:07 -05:00
f47a11f792 Updated POTFILES.in 2012-12-06 22:37:41 +01:00
36fc3a5c96 st: fix rendering of corners in the box-shadow
https://bugzilla.gnome.org/show_bug.cgi?id=689789
2012-12-06 18:42:37 +00:00
ed12c9d611 fileUtils: Don't fail if we can't find an error
We need to make sure that we decrement the number of dirs that
we've loaded, and call the callback on error.

https://bugzilla.gnome.org/show_bug.cgi?id=689749
2012-12-06 13:32:12 -05:00
4153feeb15 grabHelper: Use focus_default_window
This prevents us from having to track the previously focused window.

https://bugzilla.gnome.org/show_bug.cgi?id=689653
2012-12-06 12:25:37 -05:00
349c642d10 ctrlAltTab: Add a "Windows" option to Ctrl+Alt+Tab
This lets accessibility users easily access the desktop after focusing the panel.

https://bugzilla.gnome.org/show_bug.cgi?id=689653
2012-12-06 12:25:37 -05:00
4728105f43 ctrlAltTab: Only add to the focus manager if we have an StWidget.
https://bugzilla.gnome.org/show_bug.cgi?id=689653
2012-12-06 12:25:37 -05:00
1f46a0dc26 ctrlAltTab: Remove some dead code for a "window" parameter
This isn't set on any item, so we can safely remove it.

https://bugzilla.gnome.org/show_bug.cgi?id=689653
2012-12-06 12:25:37 -05:00
7ff7ced504 switcherPopup: Add a timestamp argument to _finish()
And use it elsewhere.

https://bugzilla.gnome.org/show_bug.cgi?id=689653
2012-12-06 12:25:37 -05:00
a43ee41b0b app-display: use a GAppInfo for the settings provider
this._gnomecc is currently unused; we actually need a GAppInfo for this
provider if we want to display an icon next to it (see future commits),
so just turn it into one.
We might move this to an external provider altogether in the future.

https://bugzilla.gnome.org/show_bug.cgi?id=689735
2012-12-06 12:15:13 -05:00
ec7ade4ee6 remote-search: implement new ActivateResult() method
This allows us to fix the shortcomings of the original ActivateResult()
method. In particular:
- allow to pass the search terms to the provider
- allow to pass a user interaction timestamp

https://bugzilla.gnome.org/show_bug.cgi?id=689735
2012-12-06 12:15:13 -05:00
9b846cbb83 search: remove additional params from activateResult() call
The only case when we're interested in using those parameters nowadays
is for DnD, which is handled in a separate method already.
Since we're not going to support DnD for non-app search results anyway,
drop the params from all the activateResults() calls; this will be
useful later since we're going to add another parameter to it.

https://bugzilla.gnome.org/show_bug.cgi?id=689735
2012-12-06 12:15:13 -05:00
2cc7fd07f8 remote-search: implement LaunchSearch() DBus method
This will be used to launch a search in the application itself.

https://bugzilla.gnome.org/show_bug.cgi?id=689735
2012-12-06 12:15:13 -05:00
72d54d9915 remote-search: first implementation of SearchProvider2
We read the implemented version from the search provider's keyfile, and
then create a RemoteSearchProvider object from the right class
accordingly.
Wire ActivateResult() to the new method (without actually passing the
new parameters along) - an actual implementation will be added in a
future commit.

https://bugzilla.gnome.org/show_bug.cgi?id=689735
2012-12-06 12:15:12 -05:00
b390b82e9d remote-search: add an org.gnome.ShellSearchProvider2 dbus interface
We are going to change the interface, so add a new version of it.
Providers will need to opt-in to the new API.
A summary of the differences compared to the previous API:
- ActivateResult() now also takes the search terms and a timestamp as
  parameters
- a new LaunchSearch() method is added

https://bugzilla.gnome.org/show_bug.cgi?id=689735
2012-12-06 12:13:24 -05:00
1ef9ee13a3 remote-search: properly document the DBus interface
Use real annotations in the XML, and use gdbus-codegen to extract those
into docbook. We then include it in the Shell's gtk-doc.

https://bugzilla.gnome.org/show_bug.cgi?id=689735
2012-12-06 12:13:24 -05:00
6822795bb8 build: Fix distcheck 2012-12-05 23:35:35 +01:00
187dec2816 sessionMode: Add support for 'parentMode' property
Defining a mode that differs significantly from the default one
can get rather cumbersome. For convenience, allow mode definitions
to inherit from an existing mode.

https://bugzilla.gnome.org/show_bug.cgi?id=689308
2012-12-05 21:56:57 +01:00
31f75e4607 extensionSystem: Add support for mode-specific extensions
As currently envisioned, the fallback replacement in 3.8 should be
a separate session at the login screen. As we will use extensions
to implement this mode, we need a way to specify extensions per
session rather than per user, so add a session-mode property for
extensions that should be loaded in addition to the user-defined
ones.

https://bugzilla.gnome.org/show_bug.cgi?id=689305
2012-12-05 21:56:57 +01:00
92083eaf76 sessionMode: Make loading of external modes asynchronous
Blocking IO on startup is bad m'kay ...

https://bugzilla.gnome.org/show_bug.cgi?id=689304
2012-12-05 21:56:57 +01:00
6b40c3974d extensionUtils: Load extensions asynchronously
Use the new collectFromDatadirsAsync() function for extension
loading as well.

https://bugzilla.gnome.org/show_bug.cgi?id=689304
2012-12-05 21:56:21 +01:00
c528401b62 remoteSearch: Factor out collectFromDatadirsAsync() utility function
Processing files from a subdirectory of various share/gnome-shell
directories asynchronously is a common enough pattern to share
the common code.

https://bugzilla.gnome.org/show_bug.cgi?id=689304
2012-12-05 21:56:20 +01:00
8a17f512f4 sessionMode: Add support for external mode definitions
Currently adding a new session mode requires patching the sources.
As defining custom modes can be desirable in some circumstances
(for instance for administrators of kiosk setups), load additional
modes from JSON files.

https://bugzilla.gnome.org/show_bug.cgi?id=689304
2012-12-05 21:56:17 +01:00
e725f8a0fe altTab: Re-implement 'switch-windows' keybinding
Now that we use the new 'switch-applications' keybinding for the
application-based alt-tab popup, we can use the 'switch-windows'
keybinding for a more traditional switcher.

Based heavily on the alternate-tab extension from Giovanni Campagna.

https://bugzilla.gnome.org/show_bug.cgi?id=688913
2012-12-05 18:42:08 +01:00
525d3c2619 altTab: Factor out thumbnail creation
https://bugzilla.gnome.org/show_bug.cgi?id=688913
2012-12-05 18:42:08 +01:00
aba46720c0 altTab: Rename AltTabPopup to AppSwitcherPopup
We will add support for a window-based popup in addition to the
current application-based one, so use a more descriptive name.

https://bugzilla.gnome.org/show_bug.cgi?id=688913
2012-12-05 18:42:08 +01:00
2fb1d707fe altTab: Use 'switch-applications' for app switcher
The Shell's alt-tab popup is application-based, so using the
'switch-windows' keybinding for it never really made sense.
Use the newly added 'switch-applications' keybinding instead.

https://bugzilla.gnome.org/show_bug.cgi?id=688913
2012-12-05 18:42:07 +01:00
dd06aa98ad gnome-shell.doap: belatedly remove me as a maintainer 2012-12-05 07:48:55 -05:00
cfb66ca153 update Punjabi Translation for master 2012-12-05 07:40:56 +05:30
0b4660702b switcherPopup: Add missing Meta declaration
This is used in the edge case if we fail a pointer grab.
2012-12-04 13:18:51 -05:00
9760227c66 Assamese translation updated 2012-12-04 15:55:17 +05:30
5faeaa2028 AppSwitcher: Remove a lost timeout on destroy
Doesn't look harmful but there's no point in running this code after
we're destroyed.

https://bugzilla.gnome.org/show_bug.cgi?id=689528
2012-12-04 10:06:37 +01:00
cd58f9f9e5 windowManager: Make use of the switch-panels-backward keybinding
We have it in the schema so there's no reason for it not to work.

https://bugzilla.gnome.org/show_bug.cgi?id=689528
2012-12-04 10:06:37 +01:00
00338bbc4b switcherPopup: Factor out altTab and ctrlAltTab's common code
https://bugzilla.gnome.org/show_bug.cgi?id=689528
2012-12-04 10:06:37 +01:00
31d14a2fb0 panel: Update the hot corner when the panel box allocation changes
This fixes the missing hotcorner in the overview.

https://bugzilla.gnome.org/show_bug.cgi?id=689537
2012-12-03 16:09:46 -05:00
49fa0ddebc layout: Use translation_y for panel animation
The anchor point is deprecated.

https://bugzilla.gnome.org/show_bug.cgi?id=689537
2012-12-03 16:06:34 -05:00
d50c3e6c67 panel: Don't create a menu for the ActivitiesButton
A "dontCreateMenu" item was created for the lock screen; we should
just use that instead of having a hacked up menu.

https://bugzilla.gnome.org/show_bug.cgi?id=689537
2012-12-03 16:06:34 -05:00
b39f56e813 messageTray: Remove an unused instance variable 2012-12-03 16:06:34 -05:00
ead0286ca6 st-private: Don't round-trip through a font-string
ClutterText can support a PangoFontDescription directly.

https://bugzilla.gnome.org/show_bug.cgi?id=689568
2012-12-03 15:56:59 -05:00
6eb05af306 workspacesView: Restore activation of (empty) workspaces
Clicking empty workspaces to leave the overview was broken by
commit 830e701d13, restore the previous behavior.

https://bugzilla.gnome.org/show_bug.cgi?id=689552
2012-12-03 19:40:29 +01:00
6b1e632621 NetworkMenu: don't notify for failed connections if cancelled by the user
If the user cancels the secret dialog, the NM will report the connection
failure, but we should not notify it as it is a user-initiated action.

https://bugzilla.gnome.org/show_bug.cgi?id=684823
2012-12-03 19:02:16 +01:00
29cb10fed8 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-12-03 12:43:46 -05:00
a88433dba6 st-private: Fix style 2012-12-02 20:11:36 -05:00
7e5f1fe411 st-private: Don't create attr lists if we don't need them
Decorations are fairly uncommon in gnome-shell, so it's
worthwhile to avoid effort creating empty attr lists. This
can also help prevent a relayout.

https://bugzilla.gnome.org/show_bug.cgi?id=689400
2012-12-02 20:11:10 -05:00
00c78d33e4 MessageTray, ScreenShield: adjust missed message counter
Fix counter size and position and add some padding in
ScreenShield notifications to not obscure the items below
the missed message counter.

https://bugzilla.gnome.org/show_bug.cgi?id=686472
2012-12-02 23:20:10 +01:00
50fbf9982e MessageTray: clean-up chat timestamps
Change the text for design reasons and correct some English issues
in the timestamp string.

https://bugzilla.gnome.org/show_bug.cgi?id=680989
2012-12-02 23:12:28 +01:00
71ad39b6e3 Add a setting for the default state of 'Remember Password'
https://bugzilla.gnome.org/show_bug.cgi?id=688039
2012-12-02 23:01:56 +01:00
3d329cdf2d Updated Tamil translation 2012-12-02 17:10:13 +05:30
21af5c8f73 Added Norwegian bokmål translation 2012-12-01 11:33:46 +01:00
716d7bb93e shell-app-usage: Fix several problems in xml parsing
Coverity rightly complained about the context < 0 comparison.
In addition, context was shadowing the parameter of the same
name, and the loops over the attributes were unnecessarily
running all the way.

https://bugzilla.gnome.org/show_bug.cgi?id=689325
2012-11-30 20:16:50 -05:00
98f4b99446 overview: Remove custom swipe scrolling implementation
https://bugzilla.gnome.org/show_bug.cgi?id=689062
2012-11-30 16:03:35 -05:00
830e701d13 workspacesView: Don't use the overview swipe scrolling system
Switch to a ClutterPanAction instead too.

https://bugzilla.gnome.org/show_bug.cgi?id=689062
2012-11-30 16:03:34 -05:00
57d9e7d5aa workspacesView: Clean up scroll code a bit
There's no code path that results in us not animating.

https://bugzilla.gnome.org/show_bug.cgi?id=689062
2012-11-30 16:03:34 -05:00
5fc16bb05b searchDisplay, appDisplay: Use ClutterPanAction for re-swipe scrolling
The sooner we can kill off the custom overview swipe scroll code, the
better.

https://bugzilla.gnome.org/show_bug.cgi?id=689062
2012-11-30 16:03:34 -05:00
cb08bd2e2e altTab: Minor code cleanup
https://bugzilla.gnome.org/show_bug.cgi?id=688913
2012-11-30 20:04:22 +01:00
961dba7faa MessageTray: don't close the tray when interacting with the right click menu
If the chosen action is not open, the tray should not be closed, to
let the user further interact with it (for example to mute or remove
more sources)

https://bugzilla.gnome.org/show_bug.cgi?id=689296
2012-11-30 18:43:57 +01:00
41f933b89e MessageTray: fix notification height expansion
MessageTray._tween removes all tweens now, so it removes also
those setting the height to expanded for critical notifications.

This is a regression from 1a27d7dfc0

https://bugzilla.gnome.org/show_bug.cgi?id=689295
2012-11-30 18:42:18 +01:00
d4dc1c87c5 dbus: Add a read-only org.gnome.Shell.Mode property
This is meant to expose the global.session_mode to applications such
as the gnome-tweak-tool, which would need it to differentiate between
the vanilla GNOME Shell mode and the fallback replacement mode.

https://bugzilla.gnome.org/show_bug.cgi?id=689300
2012-11-30 17:28:34 +01:00
e58fbeea17 appDisplay: don't increment counter if we didn't add a category
Or we'll later try to jump to the wrong category when clicking on it.
2012-11-29 19:21:41 -05:00
2492dc50db scroll-bar: Remove jitter on the scroll bar when moving the mouse
This was due to incorrect pixel clamping, which bounced the height
of the actor between values. Just remove pixel clamping, as Clutter
will correctly do it for us.

https://bugzilla.gnome.org/show_bug.cgi?id=689243
2012-11-29 16:43:47 -05:00
b4f5f1e461 scroll-view-fade: Fade gradients when near edges
Rather than sharply transitioning to gradients, fade them out when
near the edge of the scroll view.

https://bugzilla.gnome.org/show_bug.cgi?id=689249
2012-11-29 16:17:09 -05:00
69347fff09 scroll-view-fade: Only calculate the ratios if they're needed
This prevents some calculations being done for poor drivers with
bad optimizers.

https://bugzilla.gnome.org/show_bug.cgi?id=689249
2012-11-29 16:17:09 -05:00
5c439f4e9c scroll-view-fade: Do the offset math in the shader
This doesn't (or shouldn't) change the visual appearance of the fade
effect, but does do all the testing math inside the shader, rather
than on the CPU. This will make fading the offset much easier in
the future.

https://bugzilla.gnome.org/show_bug.cgi?id=689249
2012-11-29 16:17:09 -05:00
c84236ed73 scroll-view-fade: Use GLSL 1.20
GLSL 1.20 is a better language, and we'll rely on it in future updates.
This doesn't have any additional constraints, since GLSL 1.20 was
standardized before GLSL-supporting drivers came out.

https://bugzilla.gnome.org/show_bug.cgi?id=689249
2012-11-29 16:17:09 -05:00
ae1a3a0cb2 scroll-view-fade: Clean up GLSL
Basic cleanup of what we're doing already. This moves a bunch
of the "inside the fade area" checks to the top, before doing
any calculations.

https://bugzilla.gnome.org/show_bug.cgi?id=689249
2012-11-29 16:17:09 -05:00
b3fea016f1 scroll-view-fade: Put the shader in another file
This doesn't make any changes to the shader outside of adding
a header; it simply makes changes in the future easier to edit.

https://bugzilla.gnome.org/show_bug.cgi?id=689249
2012-11-29 16:17:09 -05:00
7c93639e59 scroll-view-sizing: Enable vfade by default
https://bugzilla.gnome.org/show_bug.cgi?id=689249
2012-11-29 16:17:09 -05:00
affcd5c7de scroll-view-sizing: Add a lot more boxes
https://bugzilla.gnome.org/show_bug.cgi?id=689249
2012-11-29 16:17:08 -05:00
e00eb06f66 overview: Handle sessionMode.hasOverview changes
Currently we assume that either the initial sessionMode will have
the overview or none of the pushed modes - starting without the
overview and pushing a mode that adds it fails spectacularly.
However this is exactly what we are going to do when loading external
modes asynchronously - we'll initially use the default mode while
the modes are loading, and switch to the mode passed on the command
line when finished. So make sure that the overview UI gets initialized
properly in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=689304
2012-11-29 21:31:06 +01:00
5ecc204d57 main: Fix signal name
It's SessionMode::updated, not SessionMode::update ...
2012-11-29 21:26:45 +01:00
2a5eed1eb4 viewSelector: Ignore key-presses during non-overview modals
Since commit 0c807bddaf, the run dialog no longer handles Escape
key presses itself but uses the action key mechanism of modal dialogs.
As the latter uses key-release events, our own handling of the Escape
key runs on key-press.
Fix this by bailing out early if anything has pushed a modal in addition
to the overview (like system modals, looking glass, ...).

https://bugzilla.gnome.org/show_bug.cgi?id=688196
2012-11-28 21:15:09 +01:00
d517c13d7a Telepathy: ignore errors from ack_all_pending_messages()
Invalid ID errors from that function are normal, because the set
of IDs to acknowledge may not match the set in the connection manager
due to race conditions.
This is also what empathy does.

https://bugzilla.gnome.org/show_bug.cgi?id=683449
2012-11-28 17:46:39 +01:00
bb88265d78 workspacesViews: Update new padding rules for workspacesOnlyOnPrimary
Tim Lunn fixed this for the !workspacesOnlyOnPrimary case, but not the
other.
2012-11-27 17:46:53 -05:00
858694f4cc wanda: Update code to use Panel.Animation
This simplifies our code tremendously.
2012-11-27 13:25:18 -05:00
a46321baa0 panel: Make Animation visible by default
None of the other widget actors hide themselves by default;
that's a GTK+ thing.
2012-11-27 13:25:18 -05:00
9808e8ab0d panel: Split off Animation code from AnimatedIcon
This is to share with wanda.
2012-11-27 13:16:30 -05:00
7bd7b53845 panel: Remove a bit of duplicated code
The play() method does what we want here.
2012-11-27 13:16:30 -05:00
056cfc9dc6 wanda: fix a refactor fallout
load_sliced_image() would need an additional parameter, but fold the
notify::mapped callback into the load callback while we're at it.
2012-11-27 12:56:40 -05:00
b7b60d103e Assamese translation updated 2012-11-27 14:41:28 +05:30
14fd0eb73e StWidget: don't dispose the theme node when destroyed
Theme nodes are interned and shared with other widgets, so they cannot
be disposed, otherwise we blow useful resources, and in particular we
break the parent-child chain.

https://bugzilla.gnome.org/show_bug.cgi?id=689029
2012-11-26 22:59:22 +01:00
ca73017100 extensionSystem: Don't spam the error log with OUT_OF_DATE errors
Nobody cares

https://bugzilla.gnome.org/show_bug.cgi?id=689108
2012-11-26 15:11:58 -05:00
123fb350ce screenshot: don't fire flash if we failed to take picture
https://bugzilla.gnome.org/show_bug.cgi?id=688004
2012-11-26 14:09:07 -05:00
1a27d7dfc0 MessageTray: remove all tweens when tweening for state parameters
MessageTray._tween sets the state variable to the in-progress value,
so it must be sure that at the end of the animation the value will
be the corresponding final and nothing else will happen in between.

https://bugzilla.gnome.org/show_bug.cgi?id=683986
2012-11-26 18:16:50 +01:00
b0c6d44515 Theme: improve notification scrollbar appearance
Message tray: modify bubble scrollview notification padding to
have the same padding at right, bottom, top and left since
having different padding make scrollbar looks missaligned.
Also adjust border radius of bubble to not clash the scrollbar.

https://bugzilla.gnome.org/show_bug.cgi?id=688393
2012-11-26 13:53:23 +00:00
6092e01428 Telepathy: consolidate and improve connection notifications
Use the empathy icon and name instead of the error icon, use
a friendlier title and remove the Reconnect button.

https://bugzilla.gnome.org/show_bug.cgi?id=687213
2012-11-26 13:49:54 +00:00
e20ff5ae45 Assamese translation updated 2012-11-26 15:51:37 +05:30
621a0d70e2 Updated Malayalam translations 2012-11-26 15:03:03 +05:30
855b238ec5 Make it possible to not show the users name permanently
In particular on the lock screen, this can be a privacy issue.
https://bugzilla.gnome.org/show_bug.cgi?id=688577
2012-11-24 17:11:19 -05:00
96f44e1959 WindowOverlay: animate the close button together with the border
Since the introduction of overlay hover borders, there has a been
a timing disconnection between hiding the border and button, and
this creates noise and reduces the effect of the window+overlay
as a single unit.
Solve that by animating the close button too, so that the two actors
are shown and hidden always at the same time.
Also, consolidate the code to make it clear to future authors that
those two items need to stay coordinated.

https://bugzilla.gnome.org/show_bug.cgi?id=688966
2012-11-24 18:55:47 +01:00
242dc8cddd Revert "configure.ac: relax NetworkManager requirement to 0.9.6"
This reverts commit b8a2004872.
2012-11-24 02:03:18 +09:00
b8a2004872 configure.ac: relax NetworkManager requirement to 0.9.6
0.9.7 is an unreleased version and Its actually not needed
2012-11-24 01:40:24 +09:00
63f08bf2c2 Assamese translation updated 2012-11-23 17:19:28 +05:30
4734a9ac18 Updated Hebrew translation. 2012-11-23 10:14:22 +02:00
81cd1e6c40 MessageTray: be explicit about removing tweens
Turns out that tweener has a very complex logic to decide when a new
tween on the same properties overrides completely the old, and unfortunately
what we were doing was not enough in all cases.
Just be explicit, and don't let anything else mess with the state.

https://bugzilla.gnome.org/show_bug.cgi?id=688895
2012-11-22 21:36:17 +01:00
0b9cdea7d2 Updated Dutch translation 2012-11-22 13:37:24 +01:00
f63fceb2ea workspacesDisplay: add spacing around the workspacesView
On additional monitors the workspacesView takes up the entire monitor
and in some cases windows in overview can end up hard against the
edge of the monitor.

https://bugzilla.gnome.org/show_bug.cgi?id=688133
2012-11-21 20:02:38 +01:00
f0f7165ab6 theme: Style the panel differently according to mode
https://bugzilla.gnome.org/show_bug.cgi?id=684573
2012-11-21 13:10:46 +01:00
2511f60d39 sessionMode: Add panelStyle property
The panel should change appearance according to the sessionMode,
so add a new panelStyle sessionMode property which allows to
specify a mode specific style class for the panel actors.

https://bugzilla.gnome.org/show_bug.cgi?id=684573
2012-11-21 13:10:46 +01:00
c0f868dd56 panel: Remove .in-overview style class
The panel used to be styled differently in the overview, but
nowadays this is just a left-over.

https://bugzilla.gnome.org/show_bug.cgi?id=684573
2012-11-21 13:10:46 +01:00
a112bfdaa2 panelCorner: Only overlap panel if necessary
The panel corners overlap the panel in order to hide the underline
used for active buttons where it is supposed to arc downwards
following the roundness of the corner.
Unfortunately this prevents us from using a transparent panel background,
as the overlapped area ends up with the wrong transparency. Work around
this limitation by only overlapping the panel if there is a visible
border.

https://bugzilla.gnome.org/show_bug.cgi?id=684573
2012-11-21 13:10:46 +01:00
4535a70f08 Block cancellation of the unlock dialog when dragging
Pressing esc while the mouse is down should not make the curtain fall,
otherwise a gray screen results.

https://bugzilla.gnome.org/show_bug.cgi?id=686800
2012-11-21 12:35:55 +01:00
b59529e579 MessageTray: fix closing the right click menu
We were trying to disconnect a signal we never added.

https://bugzilla.gnome.org/show_bug.cgi?id=688771
2012-11-21 12:29:43 +01:00
393c23820a unlockDialog: set password char on verif. failure
Commit c3cab28 removed bits setting the password char that was used to
allow typing the password after a failure but before the first question
for the new attempt came.

Without that code, in complex PAM setups you can end up with a visible
password.

https://bugzilla.gnome.org/show_bug.cgi?id=687113
2012-11-21 00:15:23 +01:00
e6a3958e45 appDisplay: do not show empty categories
Desktop files can specify if the application should only be shown for
specified desktops with OnlyShowIn and NotShowIn directives options.

If a menu category only contained entries for a different desktop, we
were still creating the category.

We now only add a category button if there is at least one application
to be displayed in this category.

https://bugzilla.gnome.org/show_bug.cgi?id=687970
2012-11-20 21:17:57 +01:00
59a7fdd2c9 Login: add a spinner for better process indication
We need to do a better job of indicating login process. This can
sometimes take a few seconds (particularly if you get your password
wrong): we need to give better feedback of what's going on.

This adds a spinner next to the login button if the authorization takes
some time.

https://bugzilla.gnome.org/show_bug.cgi?id=687113
2012-11-20 21:08:38 +01:00
c3cab28c9b Login: sensitivity fixes
The login dialog had these issues:
  - the entry was not really disabled, you could still edit text
  - the sensitivity state was not reset on verification failure
  - the session list was not disabled

The unlock dialog had these issues:
  - "Login as another user..." was not insensitive
  - redundant password char setting, overwriting the one given by the
    question

The entry insensitive style was also wrong.

https://bugzilla.gnome.org/show_bug.cgi?id=687113
2012-11-20 21:08:37 +01:00
4c55a6f436 modalDialog: do not launch disabled button actions
The "Sign In" button of the login dialog has its look disabled when the
entry is empty, but it can still be triggered by the Enter key.

This fixes the modal dialog so it does not trigger the action of an
insensitive button, and also means we do not need to connect to the
"activate" signal of the entry anymore.

https://bugzilla.gnome.org/show_bug.cgi?id=687113
2012-11-20 21:08:37 +01:00
c10e4c3117 modalDialog: give more control over the layout
Make the button layout public for callers to be able to have more
control over like adding custom widgets. Also, the clearButtons and
addButton methods are added as convenience for the most frequent usage.

https://bugzilla.gnome.org/show_bug.cgi?id=687113
2012-11-20 21:08:37 +01:00
3b70094151 MessageTray: fix popping up after there were modals
Open a modal dialog, try to open the message tray -> no effect, the message
tray is blocked by the dialog.
Close the dialog, try to open the message tray by pointer -> still
no effect, because the old timeout id was not cleared the first time,
so the dwell callback thinks the tray is about to open.

https://bugzilla.gnome.org/show_bug.cgi?id=688750
2012-11-20 20:04:30 +01:00
a370697385 Workspace: Highlight window clone and caption when hovered
Windows in the overview should be highlighted when hovered, to indicate
they are an active target.

Based on a patch by Marc Plano-Lesay <marc.planolesay@gmail.com>

https://bugzilla.gnome.org/show_bug.cgi?id=665310
2012-11-20 18:01:11 +01:00
8a7c0313f6 Layout: show the OSK on the monitor where the focused window lives
In multimonitor scenarios, it makes sense to show the OSK close to
the window that will actually receive keystrokes.

https://bugzilla.gnome.org/show_bug.cgi?id=685856
2012-11-20 17:57:24 +01:00
00201f7e6c networkAgent: ensure auto-register is set when the component is enabled
Setting auto-register to true launches the initial registration, and will
afterwards re-launch the agent registration if NetworkManager gets restarted.

When the component is disabled we'll first disable auto-registration, and only
then request to unregister.

https://bugzilla.gnome.org/show_bug.cgi?id=688379
2012-11-20 14:00:03 +01:00
5e3111bad8 ctrl-alt-tab: use better icons
The top panel and message tray icons were by default a gnome foot and
are replaced by better ones. The applications icon is now using the
symbolic apps icon of the dash, and the windows icon is also improved.

https://bugzilla.gnome.org/show_bug.cgi?id=641303
2012-11-20 11:42:04 +00:00
a327c10c60 Add 'No Messages' label when message tray is empty
To avoid confusion in new users, a 'No Messages' label is shown when messages
tray is empty.

https://bugzilla.gnome.org/show_bug.cgi?id=686738
2012-11-20 09:36:47 +00:00
938bc07e3c Fixed some errors in Russian translation 2012-11-20 11:47:45 +04:00
d053d45a6d messageTray: don't accidentally shift the tray around in the overview
https://bugzilla.gnome.org/show_bug.cgi?id=688422
2012-11-20 01:06:47 +01:00
5200da6eec Bump version to 3.7.2
Update NEWS
2012-11-20 00:01:40 +01:00
b0523860d9 build: Actually remove shell-screen-grabber.[ch]
Commit 7f8bfcc939 removed the code, but not the actual files,
breaking make distcheck.
2012-11-20 00:01:39 +01:00
1a1215527f messageTray: restore toggle tray in normal mode (non overview)
https://bugzilla.gnome.org/show_bug.cgi?id=688507
2012-11-20 00:01:39 +01:00
360c018c9e calendar-server: Remove useless g_type_init 2012-11-20 00:03:13 +01:00
7bed964ab2 ScreenShield: unbreak blurring the background
It was missing a uniform, so it had no effect besides desaturation.

https://bugzilla.gnome.org/show_bug.cgi?id=682536
2012-11-19 20:13:36 +01:00
989d0a5682 bluetooth: add translation hints
A couple of translations were hard to translate.

https://bugzilla.gnome.org/show_bug.cgi?id=638351
2012-11-19 19:56:08 +01:00
b121c25184 MessageTray: restore opacity when expanding a notification
MessageTray tweens both opacity and y to hide or show _notificationWidget,
but only y when expanding it. This means that an existing tween to hide
the notification will continue running, clearing the notification state.
If the hiding one completes before the showing one, the onComplete handler
will throw an exception (because the notification was nullified) and
therefore break the state tracking.

https://bugzilla.gnome.org/show_bug.cgi?id=683986
2012-11-19 19:20:31 +01:00
e0d127b3e4 shell-recorder-src: Set frame duration
We where not setting a duration for the frames, which caused the resulting videos to
have a broken header.

Fix that by making the source a live source and setting the correct frame duration.

https://bugzilla.gnome.org/show_bug.cgi?id=688487
2012-11-19 19:17:31 +01:00
a0c56c74e1 test-recoder: Use webm file extension
This matches our default pipeline.

https://bugzilla.gnome.org/show_bug.cgi?id=688487
2012-11-19 19:17:23 +01:00
f4b61a7f24 recorder: Show icon and indicator on the primary monitor
https://bugzilla.gnome.org/show_bug.cgi?id=688470
2012-11-19 19:16:24 +01:00
b2400fb883 build: require gsettings-desktop-schemas 3.7.2
For shell search provider settings schema.
2012-11-19 12:01:13 -05:00
f25832d1b6 view-selector: add support for disable-external search setting
https://bugzilla.gnome.org/show_bug.cgi?id=687491
2012-11-19 11:45:21 -05:00
cf363171aa remote-search: apply sort-order from GSettings
Sort providers according to the GSettings state, and make sure to reload
them as soon as the sort order changes.

https://bugzilla.gnome.org/show_bug.cgi?id=687491
2012-11-19 11:45:21 -05:00
38c8569d16 remote-search: restructure remote search provider loading process
Instead of adding search providers to the system as we find them, wait
until we loaded information from all the directories, and then add all
providers at once.
This will be useful when we will sort the providers information
according to the sort order saved in GSettings.

https://bugzilla.gnome.org/show_bug.cgi?id=687491
2012-11-19 11:45:21 -05:00
9791d15f39 view-selector: filter out disabled search providers
If the added search provider has an appId in the list of the disabled
search providers settings, ignore it.

https://bugzilla.gnome.org/show_bug.cgi?id=687491
2012-11-19 11:45:21 -05:00
80e7f5832b search: add API to get a list of remote providers
This will be used to reload them in case the configuration changes.

https://bugzilla.gnome.org/show_bug.cgi?id=687491
2012-11-19 11:45:21 -05:00
6e46ddaad3 search: propagate GAppInfo to SearchProvider
Save the GAppInfo in the provider object; this will be used when
filtering.

https://bugzilla.gnome.org/show_bug.cgi?id=687491
2012-11-19 11:45:21 -05:00
8499e4aab2 [l10n] Updated Estonian translation 2012-11-19 16:41:52 +02:00
5eae1851a1 Login dialog - tweak the user name text shadow
The shadow under the user name should suggest that the light source
is directly above.

https://bugzilla.gnome.org/show_bug.cgi?id=688557
2012-11-17 23:54:35 +00:00
c48a246ccb panel: programmatic anim. control of AnimatedIcon
The AnimatedIcon does not have an API for controlling the animation but
relies on the :visible property changes to start and stop a timeout used
to update the frame.

This has the inconvenient of having a side effect when visible is set to
true multiple times, and is not really the API expected from such
component. Also, there is a race if it is displayed before the images
are loaded: there is no child yet and thus we get this._frame = NaN
which leads to a crash.

Switch to a play/stop API instead, and add a load event callback to the
TextureCache.load_slice_image to exactly know when we can start using
the images.

https://bugzilla.gnome.org/show_bug.cgi?id=687583
2012-11-17 22:26:55 +01:00
f084011a61 viewSelector: Make toggle-application-view binding available in overview
https://bugzilla.gnome.org/show_bug.cgi?id=688202
2012-11-17 01:52:52 +01:00
bd40cf194c recorder: Make toggle-recorder action available in the overview
There's not really a good reason to not allow starting a recording
in the overview ...

https://bugzilla.gnome.org/show_bug.cgi?id=688202
2012-11-17 01:52:51 +01:00
2434af7e8a messageTray: Make toggle-message-tray available when up
It makes so much sense that the setting even has 'toggle' in its name.

https://bugzilla.gnome.org/show_bug.cgi?id=688202
2012-11-17 01:52:51 +01:00
ced7fa9f01 Add screenshield and unlock dialog to ctrl-alt-tab
As we now allow the ctrl-alt-tab popup on the lock screen, it should
be possible to navigate back from the top bar, so add the corresponding
elements to the switcher.

https://bugzilla.gnome.org/show_bug.cgi?id=688202
2012-11-17 01:52:51 +01:00
28b559e812 windowManager: Replace sessionMode.allowKeybindingsWhenModal
The original condition the property was based on was added to make
the a11y switcher available in the login screen, though it did never
work properly - after popping up the switcher, additional tab key
presses were ignored. As we are now able to filter bindings much more
selectively, we can simplify the check and drop the sessionMode property.

https://bugzilla.gnome.org/show_bug.cgi?id=688202
2012-11-17 01:52:51 +01:00
76d776245b windowManager: Use generic filter mechanism for all keybindings
With the new flexible system in place, there's no point explicitly
hardcoding some built-in keybindings; just use the generic mechanism
for everything.

https://bugzilla.gnome.org/show_bug.cgi?id=688202
2012-11-17 01:52:51 +01:00
0d9f70492e windowManager: Add a generic mechanism for filtering keybindings
Currently we hardcode the set of keybindings that are available in the
overview; add a generic mechanism to specify in which KeybindingModes
a keybinding should be available.

https://bugzilla.gnome.org/show_bug.cgi?id=688202
2012-11-17 01:52:51 +01:00
b58f502dd6 main: Add optional keybindingMode parameter to pushModal()
For now we just use it to assign an identifier to modal modes in
which we want to allow some keybindings, but we don't use it for
any actual filtering; we'll start doing this shortly.

https://bugzilla.gnome.org/show_bug.cgi?id=688202
2012-11-17 01:44:22 +01:00
5f367248c5 grabHelper: Support optional parameters to pushModal()
https://bugzilla.gnome.org/show_bug.cgi?id=688202
2012-11-17 01:44:22 +01:00
c2065cc3e2 main: Use Params for optional parameters to pushModal()
As the number of optional parameters grows, it gets more convenient
to use Params instead of passing in a lot of undefined/null/0 values.

https://bugzilla.gnome.org/show_bug.cgi?id=688202
2012-11-17 01:44:22 +01:00
034408971d windowManager: Implement keybinding_filter hook
We are currently using a hack to allow a select set of keybindings
in the overview. Implement the new MetaPlugin keybinding_filter
hook, which provides a cleaner way to achieve the same.

https://bugzilla.gnome.org/show_bug.cgi?id=688202
2012-11-17 01:44:22 +01:00
490206b5b2 NetworkMenu: rework multiple NIC support
Remove section titles for ethernet and mobile broadband, and replace
them with device status items that recognize if multiple devices are
installed in the same section, and if so automatically disambiguate.

https://bugzilla.gnome.org/show_bug.cgi?id=677142
2012-11-16 13:52:52 -05:00
d8540819af st-theme-node-drawing: Group multiple cogl_rectangle calls
Calling cogl_rectangles is more efficent then using multiple cogl_rectangle calls.

https://bugzilla.gnome.org/show_bug.cgi?id=688475
2012-11-16 19:28:47 +01:00
d1a9aec526 recorder: Remove dead code
This calls are nops (already called a few lines above) so just remove them.
2012-11-16 19:28:23 +01:00
e4b681a5a5 Updated Galician translations 2012-11-16 18:44:18 +01:00
52036871d1 StThemeNode: add a trivial-case short cut to all comparisons for equality
Bug: https://bugzilla.gnome.org/show_bug.cgi?id=687465
Reviewed-by: Jasper St. Pierre <jstpierre@mecheye.net>
2012-11-15 17:08:33 +00:00
4fc6a804f5 st_widget_recompute_style: short-circuit if the theme node is the same
It appears to be somewhat common for st_widget_style_changed() to be
called when no style-relevant attributes have, in fact, changed. Now that
we cache theme nodes, we're likely to get the same theme node back from
the cache. If we do, we don't need to waste time asking whether its
geometry and painting are equal to itself: we can just note that nothing
really changed and get on with our lives.

Bug: https://bugzilla.gnome.org/show_bug.cgi?id=687465
Reviewed-by: Jasper St. Pierre <jstpierre@mecheye.net>
2012-11-15 17:08:31 +00:00
08d2ca300a st_theme_node_copy_cached_paint_state: allow self-assignment
If you copy a theme node's paint state into itself, it should be an
inexpensive no-op. What actually happened was that we destroyed the
old paint state, re-initialized to blank, then copied the blank state
back into itself. In the process, we lost (for instance) the textures
for rounded corners.

Until I introduced the texture cache, this never actually happened,
because when st_widget_recompute_style() calls st_widget_get_theme_node(),
we'd always get a fresh theme node. Now, we get a theme node T back
from the cache, notice that paint_equal(T, T) is true, short-circuit
slightly by copying its drawing state into itself, and destroy drawing
state that we still needed.

I'm going to fix this in recompute_style() too, but as a general
principle, self-assignment ought to be harmless.

Bug: https://bugzilla.gnome.org/show_bug.cgi?id=687465
Reviewed-by: Jasper St. Pierre <jstpierre@mecheye.net>
2012-11-15 17:07:42 +00:00
dc2ec0a8f9 Keep similar theme nodes so we don't have to recompute CSS so often
Because we calculate and cache CSS properties once per StThemeNode,
and only a certain set of attributes can affect the CSS properties,
it's advantageous for as many widgets as possible to share a single
StThemeNode. Similarly, if a widget changes state and then changes back
(e.g. gaining and losing the :hover pseudo-class), it should ideally
get its original StThemeNode back again when it returns to the old
state.

Here, I'm using the StThemeContext as the location for a cache.
StThemeNodes are currently never freed: this seems OK for Shell's usage
(a finite number of IDs, classes, pseudo-classes and types).

Bug: https://bugzilla.gnome.org/show_bug.cgi?id=687465
Reviewed-by: Jasper St. Pierre <jstpierre@mecheye.net>
2012-11-14 19:24:42 +00:00
2cfed952bb Remove unsplit element classes and pseudo-classes
Bug: https://bugzilla.gnome.org/show_bug.cgi?id=687465
Reviewed-by: Jasper St. Pierre <jstpierre@mecheye.net>
2012-11-14 19:24:31 +00:00
a7da137778 st_theme_node_equal: use split (pseudo-)classes
This is a prerequisite for getting rid of the unsplit versions.

Bug: https://bugzilla.gnome.org/show_bug.cgi?id=687465
Reviewed-by: Jasper St. Pierre <jstpierre@mecheye.net>
2012-11-14 19:24:30 +00:00
5ae2f87ce9 Use pre-split element classes and pseudo-classes to optimize matching
In my testing this cuts the longest time to dispatch(), when showing the
calendar menu for the first time, from 604 to 442 milliseconds,
while reducing additional_selector_matches_style() from 32% to 13% of
CPU time used.

Bug: https://bugzilla.gnome.org/show_bug.cgi?id=687465
Reviewed-by: Jasper St. Pierre <jstpierre@mecheye.net>
2012-11-14 19:24:26 +00:00
c4f6619fbd Store CSS classes and pseudo-classes pre-split in the StThemeNode
Tokenizing every time we want to evaluate CSS seems a poor plan.

Bug: https://bugzilla.gnome.org/show_bug.cgi?id=687465
Reviewed-by: Jasper St. Pierre <jstpierre@mecheye.net>
2012-11-14 19:23:40 +00:00
65edbc1424 a11y: also set the WM HighContrast theme when switch is flipped
Now that we have a HighContrast-specific WM theme.

https://bugzilla.gnome.org/show_bug.cgi?id=688256
2012-11-13 11:24:18 -05:00
1f2d7fa28f Bluetooth: Remove ObexFTP functionality
ObexFTP browsing in gvfs is completely broken, and unmaintained.
http://www.hadess.net/2011/11/obexftp-in-gnome-non-update.html

Remove the UI for it. The more adventurous can go and use it
(as long as I don't get the bug reports).

https://bugzilla.gnome.org/show_bug.cgi?id=688160
2012-11-13 17:03:21 +01:00
2407a0c4e2 main: Stop using Metacity's keybinding files
The descriptions for keybindings shared with Metacity are now
duplicated in Mutter, so only use the latter to avoid duplicate
entries in System Settings.

https://bugzilla.gnome.org/show_bug.cgi?id=687672
2012-11-12 21:18:06 +01:00
7f8bfcc939 Remove shell-screen-grabber
The screen grabber was a workaround for an extremely slow path in Mesa
when reading back pixel data from the frame buffer. It was using pixel
buffer objects by directly calling into GL to hit a fast blit path in
Intel's driver. This should no longer be necessary with the latest
Mesa because the normal read pixels path now has a fast path to just
memcpy the data. Using PBOs in that case just adds an extra
indirection because the data is read into an intermediate buffer and
then copied back out again.

We want to be able to remove the dependency on linking against libGL
directly from Gnome Shell because that will not work if Cogl is
actually using GLES. Also libGL includes GLX which means gnome-shell
ends up with a hard dependency on Xlib which hinders the goal of
getting Gnome Shell to be a Wayland compositor.

https://bugs.freedesktop.org/show_bug.cgi?id=46631

https://bugzilla.gnome.org/show_bug.cgi?id=685915
2012-11-12 19:07:57 +01:00
fae4cb9e56 loginDialog: Fix yet another StIconType removal fallout
https://bugzilla.gnome.org/show_bug.cgi?id=688181
2012-11-12 18:45:10 +01:00
063bbb02f2 loginDialog: Remove the correct focus root from ctrl-alt-tab
Commit 3abfcda8b5 fixed the focus root passed to addGroup(),
but left the one in removeGroup() unchanged.

https://bugzilla.gnome.org/show_bug.cgi?id=688181
2012-11-12 18:45:10 +01:00
15e1470c50 polkitAgent: Expand password entry to the full width
Due to an oversight, the width of the password entry is currently
determined by the length of the message description. Fix the flags
so that the entry spans the entire width of the dialog.

https://bugzilla.gnome.org/show_bug.cgi?id=684810
2012-11-12 16:59:12 +01:00
f602993aa9 dash: Disable animations during overview transitions
When updating the dash, we already avoid all animations while the
overview is hidden. However, as we are using Main.queueDeferredWork(),
updates may be deferred up to ~20 seconds while the overview is hidden.
If the overview is entered before a queued update has taken place, it
will be run immediately on map - as the overview is visible by then,
this means animating any outstanding changes.
Work around this by skipping animations during overview transitions as
well.

https://bugzilla.gnome.org/show_bug.cgi?id=686530
2012-11-12 16:59:12 +01:00
271508c0a8 ScreenShield: fix code to detect missing unlock dialog
If the session mode has no locking support, screenshield had code to
unlock automatically, but it did so by checking the return value of
the constructor, instead of checking if the constructor was actually
callable, so it would get a TypeError before reaching the check.

https://bugzilla.gnome.org/show_bug.cgi?id=687708
2012-11-11 19:17:58 +01:00
50f96d1c9c Stop using clutter alpha
It is deprecated.

https://bugzilla.gnome.org/show_bug.cgi?id=688089
2012-11-11 17:13:38 +01:00
809544800b MessageTray: fix reentrancy when calling out to the grab helper
Similar to 683546. I'm not sure where this bug lives, but let's make
the code robust.

https://bugzilla.gnome.org/show_bug.cgi?id=683986
2012-11-10 17:36:00 +01:00
e757b06987 power: indicate 1 hour remaining correctly
Before 60 minutes remaining were displayed
as "0 minutes" now it is "1 hour".

https://bugzilla.gnome.org/show_bug.cgi?id=687958
2012-11-09 17:19:05 +01:00
6f2da1baf1 Updated Hebrew translation. 2012-11-09 11:36:39 +02:00
f30dcad8a7 messageTray: Close the notification on Escape
Now that the notifications can be focused with the keyboard, it's
important we can close the active one also this way.

https://bugzilla.gnome.org/show_bug.cgi?id=652082
2012-11-08 21:19:52 +01:00
7dc235511e messageTray: Add a notification focus keybinding
At the moment, only the mouse can be used to focus and answer a chat
notification.

This adds a new keybinding (defaults to <Super>+n) to focus and expand
the active notification.

https://bugzilla.gnome.org/show_bug.cgi?id=652082
2012-11-08 15:46:16 +01:00
644c210fe2 Rename "System Settings" to "Settings"
"System Settings" has been renamed to "Settings" in gnome-control-center.
See bug https://bugzilla.gnome.org/show_bug.cgi?id=681543

This patch does the same rename in the shell itself, as per

https://bugzilla.gnome.org/show_bug.cgi?id=687738
2012-11-07 16:05:23 +00:00
68e8f98671 .gitignore: Update to ignore the generated manpage 2012-11-07 14:39:28 +01:00
1228db6ac1 unlockDialog: better confirmation button label
If I click on "Not listed?" in the login screen, I come to a username
field with two buttons: "Cancel" and "Sign In".

Clicking on "Sign In" doesn't actually sign me in though - it takes me
to the login entry. It would be better to rename "Sign In" to "Next" for
the username stage, therefore.

Gdm emit a signal to ask a question or a secret, but we can not know if
this is the last authentication question, hence we only use "Sign In"
for secret questions which improve the situation a lot.

https://bugzilla.gnome.org/show_bug.cgi?id=687656
2012-11-07 01:26:35 +01:00
86596749fa Revert "overview: highlight windows on launcher hover"
This reverts commit 76229a3601.

Pushed by accident.
2012-11-07 00:33:32 +01:00
76229a3601 overview: highlight windows on launcher hover
When in the overview, if you move the mouse cursor over one of the
application launchers in the dash, all the unrelated windows are dimmed
both both in the window view and in the workspace view.

It helps to easily understand whether or not there are already opened
windows for this application, and where they are. It can also help in
differentiating the windows in the overview (sometimes the thumbnails
aren't precise enough to easily know which thumbnail belongs to which
application).

https://bugzilla.gnome.org/show_bug.cgi?id=657315
2012-11-07 00:27:31 +01:00
95abdeb919 dash: simplify app retrieval from AppWellIcon
This has also the benefit of getting the application even if it can not
be retrieved through AppSystem, which can happen if the runtime WMClass
does not match the one of the desktop file.

This especially looked wrong with the following commits related to the
bug.

https://bugzilla.gnome.org/show_bug.cgi?id=657315
2012-11-07 00:27:31 +01:00
df0151d338 dash: rename a local variable for clarity
createAppIcon creates an appIcon, not a 'display', which made things
unnecessarily more difficult to understand.

https://bugzilla.gnome.org/show_bug.cgi?id=657315
2012-11-07 00:26:50 +01:00
ee2f12fe81 dash: properly restore item label on popup close
We simply hide the label when the popup is opened instead of relying
on the popup state when the hover state change.

To do this we replace the flag isMenuUp by a 'menu-state-changed' signal
on the AppWellIcon. This simplifies the dash label visibility handling
code that need additional changes for the bug.

https://bugzilla.gnome.org/show_bug.cgi?id=657315
2012-11-07 00:26:11 +01:00
10c104529f remote-search: initialize the DBus proxy asynchronously
Initializing this synchronously means that we will possibly wait for the
process to be auto-activated and answering to our call.
If the process is already running it also might not answer immediately
our request, as it might be doing sync I/O.
The right thing to do is to initialize the proxy asynchronously; there
are try/catch blocks in place for when the object is not available, or
not properly initialized.

https://bugzilla.gnome.org/show_bug.cgi?id=687491
2012-11-06 18:11:56 -05:00
cbc8ec6508 remote-search: don't use g_file_query_exists()
This is called in the main thread, which we should never block for
synchronous I/O.
Since the operation we're wrapping is async already, just use
g_file_query_info_async() instead.

https://bugzilla.gnome.org/show_bug.cgi?id=687491
2012-11-06 17:47:48 -05:00
8e7758e280 remote-search: require a DesktopId field in search providers
Remote search providers install an auxiliary keyfile to specify
static information, such as the object path/bus name needed to activate
the binary. Such keyfiles also specify the application the providers
pushes results for; currently, we support two formats for application
information
- two fields, "Title" and "Icon" that specify a (translatable) title and
  an icon name for display
- one field "DesktopId" that specifies the desktop file name of the
  application backing the provider, which obsoletes the previous
  Title/Icon syntax

Since all providers in GNOME use DesktopId now, and we need to ensure a
remote search providers is always backed by an application for future
development, this commit drops the support for the older syntax.

https://bugzilla.gnome.org/show_bug.cgi?id=687491
2012-11-06 17:47:48 -05:00
9aefbd189c view-selector: add missing semicolon
https://bugzilla.gnome.org/show_bug.cgi?id=687491
2012-11-06 17:47:47 -05:00
b9d50584d8 screenshield: use the correct actor for the background
Commit 4fd690333a added the GLSL snippet to
the background actor, but then discarded the newly created actor.
2012-11-06 17:47:13 -05:00
e5f7390c09 ScreenShield: hide the cursor while the lock screen is on
While the screen is locked and the cursor is not moved, the mouse
cursor should be hidden, to indicate the machine is idle.

https://bugzilla.gnome.org/show_bug.cgi?id=682535
2012-11-06 23:10:01 +01:00
797b1ff8bb ScreenShield: account for motion velocity when hiding the lock screen
Switch from a ClutterDragAction to a ClutterGestureAction, that gives
us the velocity of mouse motion at each step, and use it to compute the
animation time for completing the hide gesture.

https://bugzilla.gnome.org/show_bug.cgi?id=682537
2012-11-06 23:04:15 +01:00
09b738045c MessageTray: change height in chat notification to have more context.
https://bugzilla.gnome.org/show_bug.cgi?id=665255
2012-11-06 21:53:28 +00:00
4fd690333a ScreenShield: blur and desaturate the screenshield background
The background is the same as the normal desktop, so we blur and
desaturate it to clearly show that it's not the normal system state.
To do so, we don't use standard ClutterEffects, to avoid the FBO
indirection. Instead, we take advantage of MetaBackgroundActor support
for GLSL code and paint the shaded background texture directly.

https://bugzilla.gnome.org/show_bug.cgi?id=682536
2012-11-06 22:50:10 +01:00
5ef5e25a7f Use "Decline" instead of "Reject" for incoming calls
This makes GNOME Shell more polite
Fixes bug #661194
2012-11-06 20:52:53 +00:00
87e8770cbc loginDialog: support disable-user-list key
In some deployments showing a user list at the login
screen is undesirable.

GDM's fallback login screen has a configuration key:

org.gnome.login-screen disable-user-list false

that causes the user-list to get hidden.

This commit adds similar functionality to the normal,
shell-based login screen.

Based on a series of patches by Marius Rieder.

https://bugzilla.gnome.org/show_bug.cgi?id=660660
2012-11-06 15:05:15 -05:00
cac9d120be loginDialog: hide session list until username is entered
Right now when a user clicks "Not Listed?" they end up
seeing a session list that gets reset after they enter their
username.

This commit hides the session list until the username has
been entered.

https://bugzilla.gnome.org/show_bug.cgi?id=660660
2012-11-06 15:02:36 -05:00
1ae0fadbf4 loginDialog: don't rely on PAM to ask for a username
For the "Not Listed?" case we will need to be able
to identify when the user has entered their username.

Once we have a way of tracking when the username is
entered, we can then defer showing the session list
too early, before the user can reliably pick a
session.

This username tracking will also be important for
implementing a disable-user-list configuration key.
If the config key gets toggled off at runtime, we'll
need to know if we're at a disruptive part of
the authentication process or not, so we know whether
we can can expose the user list right away, or wait
until the authentication conversation finishes.

Right now, we pass null in for an initial username,
and let the PAM machinery ask the user, which means we
have no good way of knowing when the username is entered.

This commit changes the "Not Listed?" code to ask the
user their username up front, before starting the PAM
conversation in much the same way we do if the user
picks a user from the user list.

https://bugzilla.gnome.org/show_bug.cgi?id=660660
2012-11-06 15:02:36 -05:00
65497e089a [l10n] Updated German translation 2012-11-06 20:15:34 +01:00
2d763bd033 st-entry: Change the pointer cursor on enter/leave events
Change the pointer cursor to an i-beam when it is inside the ClutterText.

https://bugzilla.gnome.org/show_bug.cgi?id=687130
2012-11-06 11:47:49 +00:00
4ebf396cf5 a11y: fixing a regression with the accessible name at dash items
After some changes, the tooltip label at the dash is not available
until it is visually shown. As this is not anymore a reliable
source of accessible name, we just set the accessible name
with the string used on that label.

https://bugzilla.gnome.org/show_bug.cgi?id=686583
2012-11-06 10:05:18 +01:00
6faa50d496 Add a --disable-man configure option
OSTree doesn't have DocBook stylesheets, so generating
man pages from xml causes build failures there.
2012-11-05 23:43:03 -05:00
6fcd6298c8 Use the same xsltproc parameters as other modules
Using the same parameters makes sure that the generated man
pages look more or less uniform.
2012-11-05 23:42:52 -05:00
5c16be5dbb Some updates to the man page
Mention relevant files, and how gnome-shell gets started
in the session.

https://bugzilla.gnome.org/show_bug.cgi?id=680601
2012-11-05 19:32:01 -05:00
e3abb6f178 Convert man page to Docbook
This make it easier to update the content and to transform
the man page into other output formats, like HTML.

https://bugzilla.gnome.org/show_bug.cgi?id=680601
2012-11-05 19:31:54 -05:00
70736be4eb mobile-providers: new country-specific type to gather providers
shell_mobile_providers_parse() was returning the country information split
into a hash table with providers and a hash table with country names. This
patch merges both outputs into a single per-country object, so the parse()
method now returns a GHashTable with the following element-type:
   (element-type utf8 ShellCountryMobileProvider>)
This also avoids more complex setups like returning lists inside of hash tables,
which was actually breaking either g-i or gtk-doc.

shell_mobile_providers_parse() was also modified to allow inputting the paths
of the country codes and provider list files to use. If paths are not given, the
default ones will be used. This helps us to provide test files during unit
tests.

Both the findProviderForMCCMNC() and findProviderForSid() methods are exported
out of the GSM and CDMA specific classes, and new unit tests for them are
implemented. Tests can be run manually with:
    $> ./tests/run-test.sh tests/unit/mobileProviders.js

https://bugzilla.gnome.org/show_bug.cgi?id=687356.
2012-11-05 22:20:08 +01:00
1c3e7330f3 ScreenShield: don't rely on gnome-session to hide the lightbox
If we lock before the user becomes active again, gnome-session will never
change presence from IDLE, and thus we'll never hide the lightbox.
Instead, install our own idle monitor.

https://bugzilla.gnome.org/show_bug.cgi?id=687020
2012-11-05 22:04:09 +01:00
6e4c89b310 main: hide the overview on key release
Entering the overview with the overlay key is done on key release but
exiting the overview on key press, which is inconsistent.

This change makes the overview hidden also on key release.

https://bugzilla.gnome.org/show_bug.cgi?id=683024
2012-11-05 21:14:28 +01:00
73b4a0ef5f st-theme: Optimize string_in_list
Rather than using a complicated set of function calls across
library boundaries and our own scanning logic, use strtok(),
which glibc already provides, and is probably much more optimized.

https://bugzilla.gnome.org/show_bug.cgi?id=687465
2012-11-05 15:10:31 -05:00
d88002c4ed Revert "panel: programmatic anim. control of AnimatedIcon"
This reverts commit e04a4c3923.

This commit exposed an already-existing race condition in the panel
animation code that caused the shell to crash for some people.

https://bugzilla.gnome.org/show_bug.cgi?id=687112
2012-11-05 14:31:04 -05:00
025c63c045 Implement non-linear overview shade for background
Adding a radial gradent to the dimming effect gives more depth to
the background.
Shading is computed in a GLSL fragment shader, and uses distance to
center of the screen to interpolate the darkening value to use.

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

https://bugzilla.gnome.org/show_bug.cgi?id=669798
2012-11-05 19:16:53 +01:00
b189cbd46e Updated Norwegian bokmål translation 2012-11-05 19:10:48 +01:00
a21ddb5914 gnome-shell-perf-tool: Fix various problems
* Fix wrong parameter name to on_name_appeared callbacks
* optparse doesn't just leave extra command line arguments, it
  errors out, so don't try to pass through extra arguments -
  instead add explicit passthrough for '--replace'
* Fix usage of Gio.DBusProxy
* Add a default value for --perf so that if it's not supplied
  things don't die with a mysterious error message. (This wasn't
  needed when --perf enabled perf-mode)

https://bugzilla.gnome.org/show_bug.cgi?id=687287
2012-11-05 13:08:25 -05:00
e04a4c3923 panel: programmatic anim. control of AnimatedIcon
The AnimatedIcon does not have an API for controlling the animation but
relies on the :visible property changes to start and stop a timeout used
to update the frame.

This has the inconvenient of having a side effect when visible is set to
true multiple times, and is not really the API expected from such
component.

Switch to a start/stop API instead. Also, update to the first frame at
startup while we are at it, since this is the expected behavior.

https://bugzilla.gnome.org/show_bug.cgi?id=687583
2012-11-05 15:05:14 +01:00
ae0821e07b Disable the login button when there is no input
You can't login until something has been entered in the password field.
We should therefore make the login button insensitive until you have
entered some text.

https://bugzilla.gnome.org/show_bug.cgi?id=687112
2012-11-05 10:17:46 +00:00
a508bece36 Updated Czech translation 2012-11-04 20:00:52 +01:00
2c9969a8f4 Updated Russian translation 2012-11-04 20:49:26 +04:00
dd9fc91f6f keyboard: simplify using Caribou.KeyModel.label
https://bugzilla.gnome.org/show_bug.cgi?id=687242
2012-11-03 08:57:23 +09:00
39d9838cc1 Show feedback notifications when the user is busy
Notifications that are created in response to direct user actions like
"is ready" or "'foo' has been removed from favorites" should always be
displayed even though the user has marked him/herself busy.

https://bugzilla.gnome.org/show_bug.cgi?id=662900
2012-11-02 19:25:26 +01:00
1397c7c624 build: Add missing file
Fix distcheck breakage from commit 687e1eabed.
2012-11-02 16:56:42 +01:00
c09fcba94f layout: Remove message tray pointer barrier
The barrier was introduced to make the message tray hot corner
usable in multiple monitor setups. With the hot corner gone in
3.6, the pointer barrier doesn't make much sense anymore, so
remove it.

https://bugzilla.gnome.org/show_bug.cgi?id=687457
2012-11-02 16:29:37 +01:00
c77b38fc4f telepathyClient: Fix auto-scroll to bottom
Instead of keeping track of the old adjustment.upper keep track of the
old adjustment.value that corresponded to the bottom scroll position.

This fixes the integrated chatview not always scrolling to the bottom
by removing the assumption that page_size is constant between updates,
which is not the case as the view is presented in various different ways.

https://bugzilla.gnome.org/show_bug.cgi?id=686571
2012-11-02 12:06:52 +01:00
aef9b733e5 loginDialog: drop spurious parameter
_onNotListed had an unusued, incorrect parameter.

This commit drops it.

https://bugzilla.gnome.org/show_bug.cgi?id=660660
2012-11-01 14:24:45 -04:00
348208617b Use the correct icon in the restart dialog
It currently uses system-shutdown-symbolic. view-refresh-symbolic
is a better fit in this case.

https://bugzilla.gnome.org/show_bug.cgi?id=670687
2012-11-01 16:21:52 +00:00
233 changed files with 41021 additions and 33456 deletions

4
.gitignore vendored
View File

@ -38,6 +38,7 @@ docs/reference/*/*.txt
docs/reference/*/*.types
docs/reference/*/html/
docs/reference/*/xml/
docs/reference/shell/doc-gen-*
gtk-doc.make
js/misc/config.js
intltool-extract.in
@ -45,6 +46,7 @@ intltool-merge.in
intltool-update.in
libtool
m4/
man/gnome-shell.1
omf.make
po/*.gmo
po/gnome-shell.pot
@ -84,6 +86,8 @@ src/test-theme
src/st.h
src/stamp-st.h
src/stamp-st.h.tmp
src/st-scroll-view-fade-generated.c
src/stamp-st-scroll-view-fade-generated.c
stamp-h1
tests/run-test.sh
xmldocs.make

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "src/gvc"]
path = src/gvc
url = git://git.gnome.org/libgnome-volume-control

View File

@ -1,7 +1,11 @@
# Point to our macro directory and pick up user flags from the environment
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
SUBDIRS = data js src browser-plugin tests po man docs
SUBDIRS = data js src browser-plugin tests po docs
if ENABLE_MAN
SUBDIRS += man
endif
EXTRA_DIST = \
.project \
@ -12,6 +16,7 @@ EXTRA_DIST = \
# These are files checked into Git that we don't want to distribute
DIST_EXCLUDE = \
.gitignore \
.gitmodules \
gnome-shell.doap \
HACKING \
MAINTAINERS \
@ -21,4 +26,4 @@ distcheck-hook:
@echo "Checking disted files against files in git"
@$(srcdir)/tools/check-for-missing.py $(srcdir) $(distdir) $(DIST_EXCLUDE)
DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc
DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc --enable-man

272
NEWS
View File

@ -1,3 +1,275 @@
3.7.90
======
* Let GNOME Shell work on EGL and GLES2 [Neil; #693225, #693438, #693339]
* Implement middle-click paste [Jasper; #645019]
* Fix top bar transition between session modes [Rui; #692966]
* Trigger the message tray with downward pressure [Jasper; #677215]
* Don't ask for a password on shutdown [Adel; #693385]
* Add a context menu to the message tray [Adel; #691035, #693887]
* Use unicode formatting in the date menu [Matthias; #689251]
* Use proper ellipsis instead of three dots [Jeremy; #689542]
* Tweak screen shield animation [Giovanni; #691964]
* Always hide the OSK when showing the message tray [Florian; #662687]
* Support sound in notifications [Giovanni; #642831]
* Place application popup menus above chrome [Jasper; #633620]
* Hide overview elements while searching [Cosimo; #682050]
* Implement updated IBus candidate popup designs [Rui; #691902]
* Add support for enable-animations preference [Cosimo; #655746]
* Don't always show the message tray in the overview [Cosimo; #693987]
* Improve arrangement of window previews [Adel; #690313]
* Remove builtin settings provider [Giovanni; #690824]
* Minimize fullscreen windows when they end up in the background [Adel; #693991]
* Add context menu to the background actor [Jasper; #681540]
* Handle backgrounds in the shell, improve startup animation [Ray; #682429]
* Hide universal access menu when not needed [Giovanni; #681528]
* Implement updated app picker designs [Florian; #694192]
* Improve login manager -> session transition [Ray; #694062]
* Don't use a grid layout in window picker [Adel; #694210]
* Use scroll wheel for workspace switching rather than zoom [Florian; #686639]
* Misc bug fixes and cleanups: [Jasper, Florian, Debarshi, Adel, Matthias,
Giovanni, Daiki, Rico, Bastien, Cosimo, Ray, Allan, Antonio; #693284,
#692680, #691746, #693303, #693162, #693161, #693522, #693385, #691715,
#688915, #689106, #682429, #693570, #693737, #693458, #692845, 693836,
#681540, #679925, #688227, #692773, #693909, #683288, #693854, #693746,
#693931, #693924, #693940, #693970, #693935, #693937, #693974, #693936,
#693975, #693822, #694030, #685849, #694052, #694035, #694038, #694079,
#694064, #681735, #694100, #694057, #694070, #693572, #693896, #686984,
#694123, #694125, #693756, #693757, #687556, #694215, 694062, #694227,
#694240, #694234, #694264, 694276, 694282, #694241, #689394, #694202,
#694265, #694289, #691806, #694290, #694296]
Contributors:
Jeremy Bicha, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Allan Day,
António Fernandes, Adel Gadllah, Rui Matos, Florian Müllner, Bastien Nocera,
Debarshi Ray, Neil Roberts, Jasper St. Pierre, Ray Strode, Rico Tzschichholz,
Daiki Ueno
Translations:
Yasumichi Akahoshi [ja], Yoji TOYODA [ja], Dušan Kazik [sk],
Wouter Bolsterlee [nl], Matej Urbančič [sl], Gheyret Kenji [ug],
Ivaylo Valkov [bg], Daniel Korostil [uk], Gheyret Kenji [ug],
Daniel Mustieles [es], Anish A [ml], Gil Forcada [ca],
Carles Ferrando [ca@valencia], Мирослав Николић [sr, sr@latin],
Aurimas Černius [lt], Rafael Ferreira [pt_BR], Fran Diéguez [gl],
Piotr Drąg [pl], Luca Ferretti [it], A S Alam [pa]
3.7.5
=====
* MessageTray: pass keyboard events to tray icons [Giovanni; #687425]
* network: add support for virtual devices (vlan, bond, bridge) [Dan; #677144]
* gdm: Allow right-clicking buttons for left-handed users [Jasper; #688748]
* Make list search results span all available horizontal space [Tanner; #691967]
* Make Show-Applications button depress when held down [Hashem; #692319]
* Set a max width on search results [Cosimo; #692453]
* Reserve scrollbar allocation for automatic policy [Cosimo; #686881]
* Improve scaling algorithm for window thumbnails [Jasper; #686944]
* Fix launching settings panels after g-c-c changes [Jasper; #692483]
* Stop launching applications from empty searches [Hashem; #692391]
* Implement per-source notification filtering [Giovanni; #685926]
* ScreenShield: Omit ActiveChanged() signal at end of fade [Giovanni; #691964]
* ScreenShield: Lower the shield on idle before locking [Giovanni; #692560]
* Make previews of minimized windows translucent in overview [Florian; #692999]
* windowManager: Respect icon geometry when minimizing [Florian; #692997]
* ScreenShield: Only show lock icon when actually locked [Giovanni; #693007]
* general: Use & instead of 'and' for Settings panels [Jeremy; #689590]
* network: Add support for new ModemManager1 interface [Aleksander; #687359]
* network: Handle LTE-only modems as GSM ones [Aleksander; #688144]
* mobile-providers: Port to libnm-gtk [Aleksander; #688943]
* general: Consistently use Title Case in top bar [Jeremy; #689589]
* panel: Add :overview pseudo class while in overview [Florian; #693218]
* sessionMode: Add support for mode-specific styling [Florian; #693219]
* loginManager: Make suspend a NOP in the ConsoleKit patch [Florian; #693162]
* screenShield: Inhibit suspend until the screen is locked [Florian; #686482]
* Misc bug fixes and cleanups [Jasper, Giovanni, Rui, Cosimo, Florian, Stefano,
Adel, Yanko; #691745, #691731, #690171, #689091, #691976, #691963, #684279,
#692052, #692091, #642831, #692454, #692715, #692678, #692723, #692677,
#683986, #692693, #692749, #692948, #692995, #692996, #692994, #677215,
#692586, #693067, #693031, #693049, #643111, #693161, #693220]
Contributors:
Jeremy Bicha, Giovanni Campagna, Cosimo Cecchi, Tanner Doshier,
Stefano Facchini, Adel Gadllah, Yanko Kaneti, Rui Matos, Aleksander Morgado,
Florian Müllner, Hashem Nasarat, Jasper St. Pierre, Dan Winship
Translations:
Duarte Loreto [pt], Daniel Mustieles [es], Kjartan Maraas [nb],
Nilamdyuti Goswami [as], Мирослав Николић [sr,sr@latin],
Tobias Endrigkeit [de], Fabio Tomat [fur], Matej Urbančič [sl], A S Alam [pa],
Inaki Larranaga Murgoitio [eu], Piotr Drąg [pl], Wouter Bolsterlee [nl],
Gheyret Kenji [ug], Yaron Shahrabani [he], Chao-Hsiung Liao [zh_HK,zh_TW],
Milo Casagrande [it], Benjamin Steinwender [de]
3.7.4.1
=======
* userMenu: Use show-full-name-in-top-bar setting [Bastien; #689561]
* dateMenu: Add "Open Clocks" entry [Mathieu; #644390]
* screenshot: Immediately show the flash spot [Jasper; #691875]
* Misc. bug fixes [Rico, Jeremy]
Contributors:
Jeremy Bicha, Mathieu Bridon, Bastien Nocera, Jasper St. Pierre,
Rico Tzschichholz
Translations:
Ihar Hrachyshka [be]
3.7.4
=====
* Make menu separators crisp [Giovanni, Allan; #641745]
* power: Update for new D-Bus name [Bastien; #690506]
* Add smooth scrolling support [Jasper; #687573]
* Tweak notification layout [Allan; #688506]
* Ping the active window when using the app menu [Giovanni; #684340]
* Make password entries insensitive after submission [Jasper; #690594, #690895]
* Honor lock-delay GSettings key [Giovanni, Matthias; #690766, #691170]
* Use text/calendar preferred app as the calendar app [Giovanni; #690767]
* lookingGlass: Move to an inspect() function [Jasper; #690726]
* Make OSK animation quicker, snappier [Rui; #688642]
* Allow to close chat notifications with Escape [Jasper; #690897]
* Honor org.gnome.desktop.screensaver.user-switch-enabled [Giovanni; #691042]
* Add a SelectArea() DBus method [Cosimo; #687954]
* Support non-absolute paths when saving screenshots [Cosimo; #688004]
* OSK: Fix extended keys popups [Rui; #674955]
* Don't hide or show the keyboard immediately [Rui; #688646]
* Improve padding in power menu [Giovanni; #689297]
* Add per-window input source switching [Rui; #691414]
* Misc bug fixes and cleanups [Rico, Jasper, Giovanni, Rui, Florian, Dan;
#690608, #690589, #690539, #687081, #690667, #690665, #690666, #685856,
#690858, #690895, #680414, #690965, #691019, #690590, #681376, #690180,
#685513, #689263, #691553, #691720, #691743, #691750]
Contributors:
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Allan Day, Rui Matos,
Florian Müllner, Bastien Nocera, Jasper St. Pierre, Rico Tzschichholz,
Dan Winship
Translations:
Matej Urbančič [sl], Kjartan Maraas [nb], Mattias Põldaru [et],
Yaron Shahrabani [he], Aurimas Černius [lt], Khaled Hosny [ar],
Fran Diéguez [gl], Daniel Mustieles [es], Piotr Drąg [pl], Balázs Úr [hu],
Baurzhan Muftakhidinov [kk], Tobias Endrigkeit [de], Dušan Kazik [sk],
Aron Xu [zh_CN], Gheyret Kenji [ug]
3.7.3
=====
* Add 'No Messages' label when message tray is empty [Victoria; #686738]
* Use better icons in Ctrl-Alt-Tab popup [Stéphane; #641303]
* Show the OSK on the monitor where the focused window lives [Giovanni; #685856]
* Highlight window clone and caption when hovered [Giovanni, Marc; #665310]
* Improve login process indication [Stéphane; #687113]
* Omit empty categories in apps view [Stéphane; #687970]
* Style panel differently according to mode [Florian; #684573]
* Make it possible to hide the user name [Matthias; #688577]
* Consolidate and improve chat connection notifications [Giovanni; #687213]
* Improve notification scrollbar appearance [Carlos; #688393]
* Fade scroll view fade near scrolling edges [Jasper; #689249]
* Add a read-only org.gnome.Shell.Mode property [Debarshi; #689300]
* Don't close message tray after using context menus [Giovanni; #689296]
* Port swipe-scrolling to ClutterPanAction [Jasper, Florian; #689062, #689552]
* Remember state of 'Remember Password' checkbox [Ron; #688039]
* Improve timestamp format in chat notifications [Carlos; #680989]
* Improve style of missed-messages counter [Carlos; #686472]
* Omit connection failure notifications if cancelled by user [Giovanni; #684823]
* Add window-based Alt-Tab popup [Florian; #688913]
* Support external session mode definitions [Florian; #689304]
* Support session-mode-specific extensions [Florian; #689305]
* Support 'parentMode' property in session modes [Florian; #689308]
* Support a new org.gnome.ShellSearchProvider2 DBus interface
[Cosimo; #689735, #690009]
* Add "windows" to Ctrl-Alt-Tab popup [Jasper; #689653]
* Port PopupMenu to GrabHelper [Jasper; #689109, #689954]
* Show headphone icon when headphones are plugged in [Giovanni; #675902]
* Display (non-app) search results as list [Tanner, Cosimo; #681797]
* Skip diacritical marks in search terms [Aleksander; #648587]
* Expose all engine options in input sources [Giovanni, Rui; #682318]
* Add input source switcher popup [Rui; #682315]
* Add minimal support for InfiniBand in network menu [Dan; #677150]
* Misc bug fixes and cleanups [Sebastian, Aleksander, Giovanni, Tim, Cosimo,
Florian, Matthias, Rui, Lionel, Colin, Piotr, Guillaume, Bastien, Tanner,
Carlos, Stéphane, Jakub; #688422, #688379, #688750, #688771, #686800,
#688133, #688895, #688966, #683986, #688004, #689108, #689029, #683449,
#688196, #689304, #689243, #689295, #689325, #689400, #679168, #689568,
#689537, #689528, #689749, #689789, #689353, #689820, #689868, #689778,
#689959, #688589, #688589, #689955, #687250, #689965, #690046, #690049,
#689884, #682286, #690173, #690174, #672941, #689876, #687881, #690171,
#690241, #690312, #690175, #687955, #650843, #688234, #690427
Contributors:
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Stéphane Démurget,
Guillaume Desmottes, Tanner Doshier, Piotr Drąg, Sebastian Keller,
Lionel Landwerlin, Tim Lunn, Victoria Martínez de la Cruz, Aleksander Morgado,
Florian Müllner, Bastien Nocera, Marc Plano-Lesay, Carlos Soriano Sánchez,
Jakub Steiner, Jasper St. Pierre, Colin Walters, Dan Winship, Ron Yorston
Translations:
Yuri Myasoedov [ru], Wouter Bolsterlee [nl], Yaron Shahrabani [he],
Nilamdyuti Goswami [as], Ani Peter [ml], Kjartan Maraas [nb],
Dr.T.Vasudevan [ta], A S Alam [pa], Shankar Prasad [kn], Khaled Hosny [ar],
Daniel Mustieles [es], Dušan Kazik [sk]
3.7.2
=====
* Enforce RTL in he for messages that might end up as LTR [Florian; #686630]
* gdm: Move logo into the panel [Florian; #685852]
* Hide notifications when closed button is clicked [Jasper, Florian; #682237]
* Tweak screenShield animations [Rui; #686745]
* Restore Fittsability of summary items in message tray [Florian; #686474]
* Save screencasts as recent item [Ray; #680647]
* overview: Resize window captions on content change [Giovanni, Alex; #620874]
* App search: Match GenericName too [Matthias; #687121]
* runDialog: Better match style of other modal dialogs [Florian, Allan; #687127]
* Improve the button insensitive style [Stéphane; #687110]
* network: Don't use a global switch for all VPN connections [Giovanni; #682929]
* appMenu: Update on icon theme changes [Florian; #687224]
* Show 'Log out' in more situations [Matthias; #686736]
* Add a setting to force the 'Log out' menuitem [Matthias; #686057]
* overview: Improve styling of search box [Stéphane; #686479]
* Implement 'disable-user-list' in login screen [Ray; #660660]
* Fix auto-scroll to bottom in chat notifications [Sjoerd; #686571]
* Show feedback notifications when user is busy [Stéphane; #662900]
* Disable login button when there is no input [Stéphane; #687112]
* Use non-linear overview shade for background [Giovanni, Pierre-Eric; #669798]
* Reduce blocking in compositor thread [Simon, Jasper; #687465]
* network: new country-specific type to gather providers [Aleksander; #687356]
* Update man page [Matthias; #680601]
* st-entry: Change the pointer cursor on enter/leave events [Thomas; #687130]
* screenShield: Blur and desaturate the background [Giovanni, Cosimo; #682536]
* Change height of chat notifications to have more context [Carlos; #665255]
* screenShield: Account for motion velocity when hiding [Giovanni; #682537]
* screenShield: hide the cursor while the lock screen is on [Giovanni; #682535]
* Support remote search provider settings [Cosimo; #687491]
* unlockDialog: Improve label of confirmation button [Stéphane; #687656]
* userMenu: Rename "System Settings" item to "Settings" [Elad; #687738]
* messageTray: Add keybinding to focus current notification [Stéphane; #652082]
* Remove shell-screen-grabber [Neil; #685915]
* main: Stop using Metacity's keybinding files [Florian; #687672]
* Bluetooth: Remove ObexFTP functionality [Bastien; #688160]
* a11y: Also set WM theme when HighContrast is switched on [Cosimo; #688256]
* network: Rework multiple NIC support [Giovanni; #677142]
* Rework keybindings to allow selective blocking/processing [Florian; #688202]
* recorder: Show indicator on primary monitor [Adel; #688470]
* recorder: Set frame duration to fix broken video headers [Adel; #688487]
* Misc. bugfixes and cleanups [Florian, Jasper, Giovanni, Matthew, Stéphane,
Allan, Daiki, Owen, Alejandro, Jean-François, Cosimo, Sebastian, Adel, Alban;
#686484, #686728, #686805, #686574, #686763, #682428, #687132, #685239,
#687189, #687226, #658091, #670687, #687457, #687242, #687287, #687020,
#686583, #661194, #687491, #657315, #687958, #683986, #688089, #687708,
#686530, #684810, #688181, #688475, #688557, #688507, #638351]
Contributors:
Elad Alfassa, Matthew Barnes, Alban Browaeys, Giovanni Campagna,
Cosimo Cecchi, Matthias Clasen, Allan Day, Stéphane Démurget,
Jean-François Fortin Tam, Adel Gadllah, Alex Hultman, Sebastian Keller,
Rui Matos, Simon McVittie, Aleksander Morgado, Florian Müllner,
Bastien Nocera, Pierre-Eric Pelloux-Prayer, Alejandro Piñeiro, Neil Roberts,
Sjoerd Simons, Carlos Soriano Sánchez, Jasper St. Pierre, Ray Strode,
Owen Taylor, Daiki Ueno, Thomas Wood
Translations:
Dušan Kazik [sk], Pavol Klačanský [sk], Piotr Drąg [pl], Yuri Myasoedov [ru],
Marek Černocký [cs], Kjartan Maraas [nb], Wolfgang Stöggl [de],
Yaron Shahrabani [he], Fran Diéguez [gl], Mattias Põldaru [et]
3.7.1
=====
* Add shortcut to open application view directly [Jeremy; #685738]

View File

@ -13,6 +13,14 @@ PKG_NAME="gnome-shell"
exit 1
}
# Fetch submodules if needed
if test ! -f src/gvc/Makefile.am;
then
echo "+ Setting up submodules"
git submodule init
fi
git submodule update
which gnome-autogen.sh || {
echo "You need to install gnome-common from GNOME Git (or from"
echo "your OS vendor's package manager)."

View File

@ -1,5 +1,5 @@
AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.7.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_INIT([gnome-shell],[3.7.90],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([src/shell-global.c])
@ -16,8 +16,6 @@ m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
# Checks for programs.
AC_PROG_CC
# Needed for per-target cflags, like in gnomeshell-taskpanel
AM_PROG_CC_C_O
# Initialize libtool
LT_PREREQ([2.2.6])
@ -36,6 +34,8 @@ AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
PKG_PROG_PKG_CONFIG([0.22])
AC_PATH_PROG([XSLTPROC], [xsltproc])
GLIB_GSETTINGS
# Get a value to substitute into gnome-shell.in
@ -53,29 +53,29 @@ 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 gtk+-3.0"
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes gl)
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes)
else
AC_MSG_RESULT(no)
fi
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
CLUTTER_MIN_VERSION=1.11.11
CLUTTER_MIN_VERSION=1.13.4
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=1.33.2
MUTTER_MIN_VERSION=3.7.1
GTK_MIN_VERSION=3.3.9
GJS_MIN_VERSION=1.35.4
MUTTER_MIN_VERSION=3.7.90
GTK_MIN_VERSION=3.7.9
GIO_MIN_VERSION=2.35.0
LIBECAL_MIN_VERSION=3.5.3
LIBEDATASERVER_MIN_VERSION=3.5.3
LIBEDATASERVERUI_MIN_VERSION=3.5.3
TELEPATHY_GLIB_MIN_VERSION=0.17.5
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
POLKIT_MIN_VERSION=0.100
STARTUP_NOTIFICATION_MIN_VERSION=0.11
GCR_MIN_VERSION=3.3.90
GNOME_DESKTOP_REQUIRED_VERSION=3.7.1
GNOME_DESKTOP_REQUIRED_VERSION=3.7.90
GNOME_MENUS_REQUIRED_VERSION=3.5.3
NETWORKMANAGER_MIN_VERSION=0.9.6
PULSE_MIN_VERS=2.0
# Collect more than 20 libraries for a prize!
PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
@ -87,42 +87,26 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
$recorder_modules
gdk-x11-3.0 libsoup-2.4
gl
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
gobject-introspection-1.0 >= $GOBJECT_INTROSPECTION_MIN_VERSION
libcanberra
libcanberra libcanberra-gtk3
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
libnm-glib libnm-util gnome-keyring-1
gcr-3 >= $GCR_MIN_VERSION)
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
saved_CFLAGS=$CFLAGS
saved_LIBS=$LIBS
CFLAGS=$GNOME_SHELL_CFLAGS
LIBS=$GNOME_SHELL_LIBS
AC_CHECK_FUNCS(JS_NewGlobalObject XFixesCreatePointerBarrier)
CFLAGS=$saved_CFLAGS
LIBS=$saved_LIBS
libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
gnome-keyring-1 gcr-3 >= $GCR_MIN_VERSION)
PKG_CHECK_MODULES(GNOME_SHELL_JS, gio-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.8 x11)
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.5.4)
PKG_CHECK_MODULES(GVC, libpulse >= $PULSE_MIN_VERS libpulse-mainloop-glib gobject-2.0)
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.7.4)
PKG_CHECK_MODULES(CARIBOU, caribou-1.0 >= 0.4.8)
AC_MSG_CHECKING([for bluetooth support])
PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
@ -142,32 +126,10 @@ PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedatas
AC_SUBST(CALENDAR_SERVER_CFLAGS)
AC_SUBST(CALENDAR_SERVER_LIBS)
AC_ARG_WITH(systemd,
AS_HELP_STRING([--with-systemd],
[Add systemd support]),
[with_systemd=$withval], [with_systemd=auto])
GNOME_KEYBINDINGS_KEYSDIR=`$PKG_CONFIG --variable keysdir gnome-keybindings`
AC_SUBST([GNOME_KEYBINDINGS_KEYSDIR])
PKG_CHECK_MODULES(SYSTEMD,
[libsystemd-login libsystemd-daemon],
[have_systemd=yes], [have_systemd=no])
if test "x$with_systemd" = "xauto" ; then
if test x$have_systemd = xno ; then
use_systemd=no
else
use_systemd=yes
fi
else
use_systemd=$with_systemd
fi
if test "x$use_systemd" = "xyes"; then
if test "x$have_systemd" = "xno"; then
AC_MSG_ERROR([Systemd support explicitly required, but systemd not found])
fi
AC_DEFINE(WITH_SYSTEMD, 1, [systemd support])
fi
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
MUTTER_GIR_DIR=`$PKG_CONFIG --variable=girdir libmutter`
MUTTER_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter`
@ -194,45 +156,22 @@ fi
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS
AM_PATH_GLIB_2_0()
G_IR_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0`
AC_SUBST(G_IR_SCANNER)
G_IR_COMPILER=`$PKG_CONFIG --variable=g_ir_compiler gobject-introspection-1.0`
AC_SUBST(G_IR_COMPILER)
G_IR_GENERATE=`$PKG_CONFIG --variable=g_ir_generate gobject-introspection-1.0`
AC_SUBST(G_IR_GENERATE)
GIRDIR=`$PKG_CONFIG --variable=girdir gobject-introspection-1.0`
AC_SUBST(GIRDIR)
TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)"
AC_SUBST(TYPELIBDIR)
GTK_DOC_CHECK([1.15], [--flavour no-tmpl])
# Stay command-line compatible with the gnome-common configure option. Here
# minimum/yes/maximum are the same, however.
AC_ARG_ENABLE(compile_warnings,
AS_HELP_STRING([--enable-compile-warnings=@<:@no/minimum/yes/maximum/error@:>@],[Turn on compiler warnings]),,
enable_compile_warnings=error)
changequote(,)dnl
if test "$enable_compile_warnings" != no ; then
if test "x$GCC" = "xyes"; then
case " $CFLAGS " in
*[\ \ ]-Wall[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wall" ;;
esac
case " $CFLAGS " in
*[\ \ ]-Wmissing-prototypes[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wmissing-prototypes" ;;
esac
if test "$enable_compile_warnings" = error ; then
case " $CFLAGS " in
*[\ \ ]-Werror[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Werror -Wno-error=deprecated-declarations" ;;
esac
fi
AC_ARG_ENABLE(man,
[AS_HELP_STRING([--enable-man],
[generate man pages [default=yes]])],,
enable_man=yes)
if test "$enable_man" != no; then
AC_PATH_PROG([XSLTPROC], [xsltproc])
if test -z "$XSLTPROC"; then
AC_MSG_ERROR([xsltproc is required for --enable-man])
fi
fi
changequote([,])dnl
AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
GNOME_COMPILE_WARNINGS([error])
AC_ARG_ENABLE(jhbuild-wrapper-script,
AS_HELP_STRING([--enable-jhbuild-wrapper-script],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)
@ -253,6 +192,7 @@ AC_CONFIG_FILES([
js/Makefile
src/calendar-server/evolution-calendar.desktop.in
src/Makefile
src/gvc/Makefile
browser-plugin/Makefile
tests/Makefile
po/Makefile.in

View File

@ -8,6 +8,9 @@
<KeyListEntry name="toggle-message-tray"
_description="Show the message tray"/>
<KeyListEntry name="focus-active-notification"
_description="Focus the active notification"/>
<KeyListEntry name="toggle-application-view"
_description="Show all applications"/>

View File

@ -11,7 +11,10 @@ desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
@INTLTOOL_DESKTOP_RULE@
introspectiondir = $(datadir)/dbus-1/interfaces
introspection_DATA = org.gnome.ShellSearchProvider.xml
introspection_DATA = \
org.gnome.Shell.Screenshot.xml \
org.gnome.ShellSearchProvider.xml \
org.gnome.ShellSearchProvider2.xml
themedir = $(pkgdatadir)/theme
dist_theme_DATA = \
@ -32,6 +35,7 @@ dist_theme_DATA = \
theme/gnome-shell.css \
theme/logged-in-indicator.svg \
theme/message-tray-background.png \
theme/more-results.svg \
theme/noise-texture.png \
theme/panel-button-border.svg \
theme/panel-button-highlight-narrow.svg \

View File

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

View File

@ -2,146 +2,72 @@
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
<node>
<!--
org.gnome.Shell.SearchProvider:
@short_description: Search provider interface
The interface used for integrating into GNOME Shell's search
interface. This interface is deprecated, and org.gnome.Shell.SearchProvider2 should be used instead.
-->
<interface name="org.gnome.Shell.SearchProvider">
<doc:doc>
<doc:description>
<doc:para>
The interface used for integrating into GNOME Shell's search
interface.
</doc:para>
</doc:description>
</doc:doc>
<!--
GetInitialResultSet:
@terms: Array of search terms, which the provider should treat as logical AND.
@results: An array of result identifier strings representing items which match the given search terms. Identifiers must be unique within the provider's domain, but other than that may be chosen freely by the provider.
Called when the user first begins a search.
-->
<method name="GetInitialResultSet">
<doc:doc>
<doc:description>
<doc:para>
Called when the user first begins a search.
</doc:para>
</doc:description>
</doc:doc>
<arg type="as" direction="in">
<doc:doc>
<doc:summary>
<doc:para>
Array of search terms, which the provider should treat as
logical AND.
</doc:para>
</doc:summary>
</doc:doc>
</arg>
<arg type="as" direction="out">
<doc:doc>
<doc:summary>
<doc:para>
An array of result identifier strings representing items which
match the given search terms. Identifiers must be unique within
the provider's domain, but other than that may be chosen freely
by the provider.
</doc:para>
</doc:summary>
</doc:doc>
</arg>
<arg type="as" name="terms" direction="in" />
<arg type="as" name="results" direction="out" />
</method>
<!--
GetSubsearchResultSet:
@previous_results: Array of results previously returned by GetInitialResultSet().
@terms: Array of updated search terms, which the provider should treat as logical AND.
@results: An array of result identifier strings representing items which match the given search terms. Identifiers must be unique within the provider's domain, but other than that may be chosen freely by the provider.
Called when a search is performed which is a "subsearch" of
the previous search, e.g. the method may return less results, but
not more or different results.
This allows search providers to only search through the previous
result set, rather than possibly performing a full re-query.
-->
<method name="GetSubsearchResultSet">
<doc:doc>
<doc:description>
<doc:para>
Called when a search is performed which is a "subsearch" of
the previous search, e.g. the method may return less results, but
not more or different results.
This allows search providers to only search through the previous
result set, rather than possibly performing a full re-query.
</doc:para>
</doc:description>
</doc:doc>
<arg type="as" direction="in">
<doc:doc>
<doc:summary>
<doc:para>
Array of item identifiers
</doc:para>
</doc:summary>
</doc:doc>
</arg>
<arg type="as" direction="in">
<doc:doc>
<doc:summary>
<doc:para>
Array of updated search terms, which the provider should treat as
logical AND.
</doc:para>
</doc:summary>
</doc:doc>
</arg>
<arg type="as" direction="out">
<doc:doc>
<doc:summary>
<doc:para>
An array of result identifier strings representing items which
match the given search terms. Identifiers must be unique within
the provider's domain, but other than that may be chosen freely
by the provider.
</doc:para>
</doc:summary>
</doc:doc>
</arg>
<arg type="as" name="previous_results" direction="in" />
<arg type="as" name="terms" direction="in" />
<arg type="as" name="results" direction="out" />
</method>
<!--
GetResultMetas:
@identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet()
@metas: A dictionary describing the given search result, containing a human-readable 'name' (string), along with the result identifier this meta is for, 'id' (string). 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.
Return an array of meta data used to display each given result
-->
<method name="GetResultMetas">
<doc:doc>
<doc:description>
<doc:para>
Return an array of meta data used to display each given result
</doc:para>
</doc:description>
</doc:doc>
<arg type="as" direction="in">
<doc:doc>
<doc:summary>
<doc:para>
An array of result identifiers as returned by
GetInitialResultSet() or GetSubsearchResultSet()
</doc:para>
</doc:summary>
</doc:doc>
</arg>
<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)
</doc:para>
</doc:summary>
</doc:doc>
</arg>
<arg type="as" name="identifiers" direction="in" />
<arg type="aa{sv}" name="metas" direction="out" />
</method>
<!--
ActivateResult:
@identifier: A result identifier as returned by GetInitialResultSet() or GetSubsearchResultSet()
Called when the users chooses a given result. The result should
be displayed in the application associated with the corresponding
provider.
This method is deprecated, and providers should implement ActivateResult2()
instead.
-->
<method name="ActivateResult">
<doc:doc>
<doc:description>
<doc:para>
Called when the users chooses a given result. The result should
be displayed in the application associated with the corresponding
provider.
</doc:para>
</doc:description>
</doc:doc>
<arg type="s" direction="in">
<doc:doc>
<doc:summary>
<doc:para>
A result identifier as returned by GetInitialResultSet() or
GetSubsearchResultSet()
</doc:para>
</doc:summary>
</doc:doc>
</arg>
<arg type="s" name="identifier" direction="in" />
</method>
</interface>
</node>

View File

@ -0,0 +1,87 @@
<!DOCTYPE node PUBLIC
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
<node>
<!--
org.gnome.Shell.SearchProvider2:
@short_description: Search provider interface
The interface used for integrating into GNOME Shell's search
interface (version 2).
-->
<interface name="org.gnome.Shell.SearchProvider2">
<!--
GetInitialResultSet:
@terms: Array of search terms, which the provider should treat as logical AND.
@results: An array of result identifier strings representing items which match the given search terms. Identifiers must be unique within the provider's domain, but other than that may be chosen freely by the provider.
Called when the user first begins a search.
-->
<method name="GetInitialResultSet">
<arg type="as" name="terms" direction="in" />
<arg type="as" name="results" direction="out" />
</method>
<!--
GetSubsearchResultSet:
@previous_results: Array of results previously returned by GetInitialResultSet().
@terms: Array of updated search terms, which the provider should treat as logical AND.
@results: An array of result identifier strings representing items which match the given search terms. Identifiers must be unique within the provider's domain, but other than that may be chosen freely by the provider.
Called when a search is performed which is a "subsearch" of
the previous search, e.g. the method may return less results, but
not more or different results.
This allows search providers to only search through the previous
result set, rather than possibly performing a full re-query.
-->
<method name="GetSubsearchResultSet">
<arg type="as" name="previous_results" direction="in" />
<arg type="as" name="terms" direction="in" />
<arg type="as" name="results" direction="out" />
</method>
<!--
GetResultMetas:
@identifiers: An array of result identifiers as returned by GetInitialResultSet() or GetSubsearchResultSet()
@metas: A dictionary describing the given search result, containing a human-readable 'name' (string), along with the result identifier this meta is for, 'id' (string). 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.
Return an array of meta data used to display each given result
-->
<method name="GetResultMetas">
<arg type="as" name="identifiers" direction="in" />
<arg type="aa{sv}" name="metas" direction="out" />
</method>
<!--
ActivateResult:
@identifier: A result identifier as returned by GetInitialResultSet() or GetSubsearchResultSet()
@terms: Array of search terms, which the provider should treat as logical AND.
@timestamp: A timestamp of the user interaction that triggered this call
Called when the users chooses a given result. The result should
be displayed in the application associated with the corresponding
provider. The provided search terms can be used to allow launching a full search in
the application.
-->
<method name="ActivateResult">
<arg type="s" name="identifier" direction="in" />
<arg type="as" name="terms" direction="in" />
<arg type="u" name="timestamp" direction="in" />
</method>
<!--
LaunchSearch:
@terms: Array of search terms, which the provider should treat as logical AND.
@timestamp: A timestamp of the user interaction that triggered this call
Asks the search provider to launch a full search in the application for the provided terms.
-->
<method name="LaunchSearch">
<arg type="as" name="terms" direction="in" />
<arg type="u" name="timestamp" direction="in" />
</method>
</interface>
</node>

View File

@ -39,6 +39,14 @@
will be displayed in the favorites area.
</_description>
</key>
<key name="app-folder-categories" type="as">
<default>[ 'Utilities', 'Sundry' ]</default>
<_summary>List of categories that should be displayed as folders</_summary>
<_description>
Each category name in this list will be represented as folder in the
application view, rather than being displayed inline in the main view.
</_description>
</key>
<key name="command-history" type="as">
<default>[]</default>
<_summary>History for command (Alt-F2) dialog</_summary>
@ -65,6 +73,16 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
menuitem in single-user, single-session situations.
</_description>
</key>
<key name="remember-mount-password" type="b">
<default>false</default>
<_summary>Whether to remember password for mounting encrypted or remote filesystems</_summary>
<_description>
The shell will request a password when an encrypted device or a
remote filesystem is mounted. If the password can be saved for
future use a 'Remember Password' checkbox will be present.
This key sets the default state of the checkbox.
</_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"/>
@ -106,6 +124,13 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
Keybinding to toggle the visibility of the message tray.
</_description>
</key>
<key name="focus-active-notification" type="as">
<default>["&lt;Super&gt;n"]</default>
<_summary>Keybinding to focus the active notification</_summary>
<_description>
Keybinding to focus the active notification.
</_description>
</key>
<key name="toggle-recording" type="as">
<default><![CDATA[['<Control><Shift><Alt>r']]]></default>
<_summary>Keybinding to toggle the screen recorder</_summary>
@ -164,6 +189,33 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
</key>
</schema>
<enum id="org.gnome.shell.window-switcher.AppIconMode">
<value value="1" nick="thumbnail-only"/>
<value value="2" nick="app-icon-only"/>
<value value="3" nick="both"/>
</enum>
<schema id="org.gnome.shell.window-switcher"
path="/org/gnome/shell/window-switcher/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="app-icon-mode" enum="org.gnome.shell.window-switcher.AppIconMode">
<default>'both'</default>
<_summary>The application icon mode.</_summary>
<_description>
Configures how the windows are shown in the switcher. Valid possibilities
are 'thumbnail-only' (shows a thumbnail of the window), 'app-icon-only'
(shows only the application icon) or 'both'.
</_description>
</key>
<key type="b" name="current-workspace-only">
<default>false</default>
<summary>Limit switcher to current workspace.</summary>
<description>
If true, only windows from the current workspace are shown in the switcher.
Otherwise, all windows are included.
</description>
</key>
</schema>
<schema id="org.gnome.shell.overrides" path="/org/gnome/shell/overrides/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="attach-modal-dialogs" type="b">

View File

@ -38,7 +38,6 @@ stage {
/* small */
.app-well-menu,
.contact-details-status,
.run-dialog-error-label {
font-size: 9pt;
}
@ -49,7 +48,7 @@ stage {
.switcher-list,
.app-well-app > .overview-icon,
.show-apps > .overview-icon,
.search-result-content > .overview-icon {
.grid-search-result .overview-icon {
font-size: 9pt;
font-weight: bold;
}
@ -128,7 +127,7 @@ StScrollBar StButton#vhandle:active {
.popup-menu-boxpointer {
-arrow-border-radius: 8px;
-arrow-background-color: black;
-arrow-background-color: rgba(0,0,0,0.9);
-arrow-border-width: 2px;
-arrow-border-color: #a5a5a5;
-arrow-base: 24px;
@ -206,11 +205,12 @@ StScrollBar StButton#vhandle:active {
}
.popup-separator-menu-item {
-gradient-height: 2px;
-gradient-start: rgba(8,8,8,0);
-gradient-end: #333333;
-margin-horizontal: 1.5em;
height: 1em;
-gradient-height: 1px;
-gradient-start: rgba(255,255,255,0.0);
-gradient-end: rgba(255,255,255,0.3);
-margin-horizontal: 24px;
height: 1px;
padding: 8px 0px;
}
.popup-alternating-menu-item:alternate {
@ -251,6 +251,10 @@ StScrollBar StButton#vhandle:active {
icon-size: 1.09em;
}
.popup-battery-percentage {
padding-left: 24px;
}
/* Switches */
.toggle-switch {
width: 65px;
@ -281,20 +285,24 @@ StScrollBar StButton#vhandle:active {
/* Buttons */
.dash-search-button,
.candidate-page-button,
.notification-button,
.notification-icon-button,
.hotplug-notification-item,
.hotplug-resident-eject-button,
.modal-dialog-button {
font-weight: bold;
.modal-dialog-button,
.app-view-control {
border: 1px solid #8b8b8b;
background-gradient-direction: vertical;
background-gradient-start: rgba(255, 255, 255, 0.2);
background-gradient-end: rgba(255, 255, 255, 0);
}
.dash-search-button:hover,
.modal-dialog-button {
font-weight: bold;
}
.candidate-page-button:hover,
.notification-button:hover,
.notification-icon-button:hover,
.hotplug-notification-item:hover,
@ -304,8 +312,6 @@ StScrollBar StButton#vhandle:active {
background-gradient-end: rgba(255, 255, 255, 0.1);
}
.dash-search-button:selected,
.dash-search-button:focus,
.notification-button:focus,
.notification-icon-button:focus,
.hotplug-notification-item:focus,
@ -313,18 +319,20 @@ StScrollBar StButton#vhandle:active {
border-width: 2px;
}
.dash-search-button:active,
.dash-search-button:pressed,
.candidate-page-button:active,
.candidate-page-button:pressed,
.notification-button:active,
.notification-icon-button:active,
.hotplug-notification-item:active,
.hotplug-resident-eject-button:active,
.modal-dialog-button:active,
.modal-dialog-button:pressed {
.modal-dialog-button:pressed,
.app-view-control:checked {
background-gradient-start: rgba(255, 255, 255, 0);
background-gradient-end: rgba(255, 255, 255, 0.2);
}
.candidate-page-button:insensitive,
.notification-button:insensitive,
.notification-icon-button:insensitive,
.modal-dialog-button:insensitive {
@ -334,6 +342,27 @@ StScrollBar StButton#vhandle:active {
background-color: rgba(102, 102, 102, 0.15);
}
/* Common radii */
#searchEntry,
.modal-dialog-button,
.notification-button,
.hotplug-notification-item,
.app-view-controls {
border-radius: 18px;
}
.app-view-control:first-child:ltr,
.app-view-control:last-child:rtl {
border-radius: 18px 0px 0px 18px;
border-right-width: 0px;
}
.app-view-control:last-child:ltr,
.app-view-control:first-child:rtl {
border-radius: 0px 18px 18px 0px;
}
/* Entries */
#searchEntry,
@ -354,7 +383,7 @@ StScrollBar StButton#vhandle:active {
background-gradient-start: rgba(5,5,6,0.1);
background-gradient-end: rgba(254,254,254,0.1);
background-gradient-direction: vertical;
transition-duration: 300;
transition-duration: 300ms;
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6);
}
@ -389,7 +418,7 @@ StScrollBar StButton#vhandle:active {
color: rgb(64, 64, 64);
caret-color: rgb(64, 64, 64);
font-weight: bold;
transition-duration: 0;
transition-duration: 0ms;
}
.notification StEntry,
@ -405,11 +434,13 @@ StScrollBar StButton#vhandle:active {
padding: 0 4px;
}
.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);
.modal-dialog StEntry:insensitive {
border-color: #666666;
color: #9f9f9f;
border: 2px solid #9f9f9f;
background-gradient-direction: none;
background-color: rgba(102, 102, 102, 0.15);
box-shadow: inset 0 0 rgba(0,0,0,1.0);
}
/* Panel */
@ -420,6 +451,15 @@ StScrollBar StButton#vhandle:active {
height: 1.86em;
}
#panel.lock-screen {
background-color: rgba(0,0,0,0.3);
}
#panel.unlock-screen,
#panel.login-screen {
background-color: transparent;
}
#panelLeft, #panelCenter {
spacing: 4px;
}
@ -453,6 +493,13 @@ StScrollBar StButton#vhandle:active {
-panel-corner-border-color: rgba(255,255,255,0.8);
}
.panel-corner.lock-screen,
.panel-corner.unlock-screen,
.panel-corner.login-screen {
-panel-corner-background-color: transparent;
-panel-corner-border-color: transparent;
}
#appMenu {
spacing: 4px;
}
@ -482,7 +529,25 @@ StScrollBar StButton#vhandle:active {
-minimum-hpadding: 6px;
font-weight: bold;
color: #ccc;
transition-duration: 100;
transition-duration: 100ms;
}
#panel.unlock-screen .panel-button,
#panel.lock-screen .panel-button,
#panel.login-screen .panel-button {
color: #e6e6e6;
}
#panel.unlock-screen .panel-button:hover,
#panel.lock-screen .panel-button:hover,
#panel.login-screen .panel-button:hover,
#panel.unlock-screen .panel-button:active,
#panel.lock-screen .panel-button:active,
#panel.login-screen .panel-button:active,
#panel.unlock-screen .panel-button:focus,
#panel.lock-screen .panel-button:focus,
#panel.login-screen .panel-button:focus {
color: white;
}
.panel-button:hover {
@ -512,13 +577,17 @@ StScrollBar StButton#vhandle:active {
}
.panel-menu {
-boxpointer-gap: 4px
-boxpointer-gap: 4px;
}
.panel-status-button-box {
spacing: 4px;
}
.lock-screen-status-button-box {
spacing: 8px;
}
/* User Menu */
#panelUserMenu {
@ -574,15 +643,11 @@ StScrollBar StButton#vhandle:active {
/* Overview */
#overview {
spacing: 40px;
spacing: 24px;
}
.window-caption {
spacing: 25px;
}
.workspace-controls {
visible-width: 32px; /* Amount visible before hovering */
.overview-controls {
padding-bottom: 32px;
}
.workspace-thumbnails-background {
@ -602,6 +667,7 @@ StScrollBar StButton#vhandle:active {
.workspace-thumbnails {
spacing: 11px;
visible-width: 32px; /* Amount visible before hovering */
}
.workspace-thumbnail-indicator {
@ -611,10 +677,16 @@ StScrollBar StButton#vhandle:active {
}
.window-caption {
spacing: 25px;
background: rgba(0,0,0,0.5);
border-radius: 8px;
padding: 4px 12px;
-shell-caption-spacing: 12px;
border: 2px solid rgba(0, 0, 0, 0);
}
.window-caption:hover {
border: 2px solid rgba(255, 255, 255, 0);
}
.window-close, .notification-close {
@ -625,7 +697,12 @@ StScrollBar StButton#vhandle:active {
}
.window-close {
-shell-close-overlap: 20px;
-shell-close-overlap: 16px;
}
.window-clone-border {
border: 4px solid rgba(255, 255, 255, 0.5);
border-radius: 4px;
}
.notification-close {
@ -653,8 +730,38 @@ StScrollBar StButton#vhandle:active {
}
.window-picker {
-horizontal-spacing: 40px;
-vertical-spacing: 40px;
-horizontal-spacing: 32px;
-vertical-spacing: 32px;
padding-left: 32px;
padding-right: 32px;
padding-bottom: 48px;
}
.window-picker.external-monitor {
padding: 32px;
}
.messages-indicator {
color: #999999;
height: 32px;
}
.messages-indicator-contents {
spacing: 12px;
padding-bottom: 12px;
}
.messages-indicator-contents:hover {
color: white;
text-shadow: black 0px 2px 2px;
}
.messages-indicator-highlight {
background-gradient-start: transparent;
background-gradient-end: #999999;
background-gradient-direction: vertical;
height: 6px;
}
/* Dash */
@ -681,14 +788,9 @@ StScrollBar StButton#vhandle:active {
height: 24px;
}
#viewSelector {
spacing: 1em;
}
/* Search Box */
#searchEntry {
border-radius: 17px;
width: 320px;
}
@ -705,25 +807,36 @@ StScrollBar StButton#vhandle:active {
/* Search Results */
#searchResults {
padding: 20px 10px 10px 10px;
padding: 20px 10px 0px 10px;
spacing: 18px;
}
#searchResultsBin {
max-width: 1000px;
}
#searchResultsContent {
padding-right: 20px;
spacing: 36px;
}
#searchResultsContent:rtl {
padding-right: 0px;
padding-left: 20px;
padding-right: 20px;
spacing: 16px;
}
.search-section-header {
padding: 4px 12px;
spacing: 4px;
color: #6f6f6f;
font-size: .8em;
.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: 32px;
}
.search-statustext {
@ -732,34 +845,8 @@ StScrollBar StButton#vhandle:active {
font-weight: bold;
}
.search-section-results {
padding: 6px;
}
.search-section-list-results {
spacing: 4px;
}
.results-container {
spacing: 4px;
}
/* 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 */
.dash-search-button {
border-radius: 16px;
padding-top: 4px;
padding-bottom: 5px;
width: 300px;
font-weight: bold;
}
.dash-search-button:focus,
.dash-search-button:selected {
padding-top: 3px;
padding-bottom: 4px;
width: 298px;
.list-search-results {
spacing: 3px;
}
.dash-label {
@ -770,7 +857,7 @@ StScrollBar StButton#vhandle:active {
-x-offset: 8px;
}
/* Application Launchers and Grid */
/* Application Launchers, Grid and List results */
.icon-grid {
spacing: 36px;
@ -782,71 +869,104 @@ StScrollBar StButton#vhandle:active {
icon-size: 96px;
}
.all-app {
padding: 16px 25px 16px 16px;
.app-display {
padding: 8px;
spacing: 20px;
}
.all-app:rtl {
padding-right: 16px;
padding-left: 25px;
.app-view-controls {
width: 250px;
padding-bottom: 32px;
}
.app-filter {
font-weight: bold;
height: 2.85em;
color: #aaa;
width: 200px;
.app-view-control {
padding: 4px 16px;
}
.app-filter:hover {
color: #eee;
.search-display > StBoxLayout,
.all-apps > StBoxLayout,
.frequent-apps > StBoxLayout {
/* horizontal padding to make sure the scrollbar doesn't overlap content */
padding: 0px 18px;
}
.app-filter:selected {
color: #ffffff;
background-image: url("filter-selected-ltr.svg");
background-position: 190px 10px;
}
.app-filter:selected:rtl {
background-image: url("filter-selected-rtl.svg");
background-position: 10px 10px;
}
.app-filter:focus {
outline: 1px solid #aaa;
.app-folder-icon {
padding: 5px;
}
.dash-item-container > StButton {
padding: 4px 8px;
}
.list-search-result-content {
spacing: 12px;
padding: 12px;
}
.list-search-result-title {
font-weight: bold;
font-size: 14pt;
color: white;
}
.list-search-result-description {
color: #eeeeec;
}
.search-provider-icon-more {
width: 16px;
height: 16px;
background-image: url("more-results.svg");
}
.app-well-app > .overview-icon,
.show-apps > .overview-icon,
.search-result-content > .overview-icon {
.search-provider-icon,
.list-search-result,
.grid-search-result .overview-icon {
border-radius: 4px;
padding: 3px;
border: 1px rgba(0,0,0,0);
transition-duration: 100;
transition-duration: 100ms;
text-align: center;
}
.app-folder-popup {
-arrow-border-radius: 8px;
-arrow-background-color: black;
-arrow-base: 24px;
-arrow-rise: 11px;
}
.app-folder-popup-bin {
padding: 15px;
}
.app-well-app.running > .overview-icon {
text-shadow: black 0px 2px 2px;
background-image: url("running-indicator.svg");
background-size: contain;
}
.app-well-app.app-folder > .overview-icon {
background-color: rgba(0,0,0,0.5);
}
.app-well-app:hover > .overview-icon,
.show-apps:hover > .overview-icon,
.search-result-content:hover > .overview-icon {
.search-provider-icon:hover,
.list-search-result:hover,
.grid-search-result:hover .overview-icon {
background-color: rgba(255,255,255,0.1);
text-shadow: black 0px 2px 2px;
transition-duration: 100;
transition-duration: 100ms;
color:white;
}
.list-search-result:hover .list-search-result-description {
text-shadow: rgba(0,0,0,0.8) 0px 1px 2px;
}
.show-apps {
padding: 4px 0;
}
@ -859,26 +979,33 @@ StScrollBar StButton#vhandle:active {
color: white;
}
.show-apps:checked > .overview-icon {
.app-well-app:checked > .overview-icon,
.app-well-app:active > .overview-icon,
.show-apps:checked > .overview-icon,
.show-apps:active > .overview-icon {
background-gradient-start: rgba(255, 255, 255, .05);
background-gradient-end: rgba(255, 255, 255, .15);
background-gradient-direction: vertical;
border-radius: 4px;
box-shadow: inset 0px 1px 2px 0px rgba(0, 0, 0, 1);
transition-duration: 100;
transition-duration: 100ms;
}
.show-apps:checked .show-apps-icon,
.show-apps:focus .show-apps-icon {
color: white;
transition-duration: 100;
transition-duration: 100ms;
}
.app-well-app:focus > .overview-icon,
.search-result-content:focus > .overview-icon,
.grid-search-result:focus .overview-icon,
.show-apps:focus > .overview-icon,
.search-provider-icon:focus,
.list-search-result:focus,
.app-well-app:selected > .overview-icon,
.search-result-content:selected > .overview-icon {
.grid-search-result:selected .overview-icon,
.search-provider-icon:selected,
.list-search-result:selected {
background-color: rgba(255,255,255,0.33);
}
@ -906,7 +1033,7 @@ StScrollBar StButton#vhandle:active {
-minimum-hpadding: 6px;
font-weight: bold;
color: #ccc;
transition-duration: 100;
transition-duration: 100ms;
padding-left: .3em;
padding-right: .3em;
}
@ -1209,24 +1336,24 @@ StScrollBar StButton#vhandle:active {
#message-tray {
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-summary {
height: 72px;
}
#message-tray:overview {
background: rgba(0, 0, 0, 0.1);
outline: 1px solid rgba(128, 128, 128, 0.3);
.no-messages-label {
font-family: cantarell, sans-serif;
font-size: 11pt;
color: #999999;
}
.notification {
border-radius: 10px 10px 0px 0px;
background: rgba(0,0,0,0.8);
background: rgba(0,0,0,0.9);
padding: 8px 8px 4px 8px;
spacing-rows: 10px;
spacing-rows: 4px;
spacing-columns: 10px;
}
@ -1255,8 +1382,8 @@ StScrollBar StButton#vhandle:active {
}
.summary-boxpointer {
-arrow-border-radius: 8px;
-arrow-background-color: rgba(0,0,0,0.8);
-arrow-border-radius: 15px;
-arrow-background-color: rgba(0,0,0,0.9);
-arrow-base: 36px;
-arrow-rise: 18px;
color: white;
@ -1276,8 +1403,8 @@ StScrollBar StButton#vhandle:active {
.summary-notification-stack-scrollview {
max-height: 18em;
padding-top: 6px;
padding-bottom: 6px;
padding-top: 8px;
padding-bottom: 8px;
}
.summary-notification-stack-scrollview:ltr {
@ -1306,11 +1433,11 @@ StScrollBar StButton#vhandle:active {
}
.notification-actions {
padding-top: 18px;
spacing: 10px;
}
.notification-button {
border-radius: 18px;
padding: 4px 42px 5px;
}
@ -1343,7 +1470,6 @@ StScrollBar StButton#vhandle:active {
.hotplug-notification-item {
padding: 2px 10px;
border-radius: 18px;
}
.hotplug-notification-item:focus {
@ -1432,6 +1558,10 @@ StScrollBar StButton#vhandle:active {
padding-right: 4px;
}
.chat-notification-scrollview{
max-height: 22em;
}
.subscription-message {
font-style: italic;
}
@ -1440,10 +1570,6 @@ StScrollBar StButton#vhandle:active {
border-radius: 4px;
}
#summary-mode {
height: 72px;
}
.summary-source-button {
padding: 6px 3px 6px 3px;
}
@ -1468,26 +1594,40 @@ StScrollBar StButton#vhandle:active {
.summary-source {
border-radius: 4px;
padding: 0 6px 0 6px;
transition-duration: 100;
transition-duration: 100ms;
}
.summary-source-counter {
background-image: url("summary-counter.svg");
background-size: 2.5em;
background-size: 2.4em;
font-size: 10pt;
font-weight: bold;
height: 2.5em;
width: 2.5em;
-shell-counter-overlap-x: 4px;
-shell-counter-overlap-y: 4px;
height: 2.4em;
width: 2.4em;
-shell-counter-overlap-x: 13px;
-shell-counter-overlap-y: 13px;
}
/* OSD */
.osd-window {
text-align: center;
font-weight: bold;
spacing: 1em;
}
.osd-window .level {
height: 0.6em;
border-radius: 0.3em;
background-color: rgba(190,190,190,0.2);
}
/* App Switcher */
#altTabPopup {
.switcher-popup {
padding: 8px;
spacing: 16px;
}
.osd-window,
.switcher-list {
background: rgba(0,0,0,0.8);
border: 1px solid rgba(128,128,128,0.40);
@ -1639,8 +1779,6 @@ StScrollBar StButton#vhandle:active {
}
.modal-dialog-button {
border-radius: 18px;
margin-left: 10px;
margin-right: 10px;
padding: 4px 32px 5px;
@ -1991,17 +2129,64 @@ StScrollBar StButton#vhandle:active {
}
/* IBus Candidate Popup */
.candidate-popup-boxpointer {
-arrow-border-radius: 8px;
-arrow-background-color: #707070;
-arrow-border-width: 0px;
-arrow-base: 24px;
-arrow-rise: 11px;
}
.candidate-popup-content {
padding: 0.5em;
spacing: 0.3em;
}
.candidate-popup-text {
font-size: 9pt;
}
.candidate-index {
padding: 0.5em 0.5em 0.5em 0.5em;
padding: 0 0.5em 0 0;
color: #cccccc;
}
.candidate-label {
padding: 0.5em 0.5em 0.5em 0.5em;
.candidate-box {
padding: 0.3em 0.5em 0.3em 0.5em;
}
.candidate-label:selected {
.candidate-box:selected {
border-radius: 4px;
background-color: rgba(255,255,255,0.33);
background-color: rgba(255,255,255,0.2);
}
.candidate-box:hover {
border-radius: 4px;
background-color: rgba(255,255,255,0.1);
}
.candidate-page-button-box {
height: 2em;
width: 80px;
}
.vertical .candidate-page-button-box {
padding-top: 0.5em;
}
.horizontal .candidate-page-button-box {
padding-left: 0.5em;
}
.candidate-page-button-previous {
border-radius: 4px 0px 0px 4px;
}
.candidate-page-button-next {
border-radius: 0px 4px 4px 0px;
}
.candidate-page-button-icon {
icon-size: 1em;
}
/* Login Dialog */
@ -2125,7 +2310,7 @@ StScrollBar StButton#vhandle:active {
font-weight: bold;
text-align: left;
padding-left: 15px;
text-shadow: black 4px 4px 3px 0px;
text-shadow: black 0px 4px 3px 0px;
}
.login-dialog-prompt-layout {
@ -2191,6 +2376,10 @@ StScrollBar StButton#vhandle:active {
height: .75em;
}
.login-dialog .modal-dialog-button-box {
spacing: 3px;
}
.login-dialog .modal-dialog-button {
border-radius: 5px;
padding: 3px 18px;
@ -2233,10 +2422,18 @@ StScrollBar StButton#vhandle:active {
color: orange;
}
.unlock-dialog-user-name-container {
.user-widget {
spacing: .4em;
}
.user-widget-label {
font-size: 16pt;
font-weight: bold;
text-align: left;
padding-left: 15px;
text-shadow: black 0px 4px 3px 0px;
}
/* Screen shield */
.screen-shield-background {
@ -2293,19 +2490,25 @@ StScrollBar StButton#vhandle:active {
.screen-shield-notifications-box {
spacing: 18px;
max-width: 34em;
}
.screen-shield-notification-source {
padding: 0 24px;
padding: 13px 24px;
spacing: 5px;
}
.screen-shield-notification-label {
font-size: 1.2em;
font-weight: bold;
padding: 0px 18px;
color: #babdb6;
}
.screen-shield-notification-count-text {
padding: 0px 18px;
}
/* Remove background from notifications, otherwise
opacity is doubled and they look darker
*/
@ -2321,3 +2524,14 @@ StScrollBar StButton#vhandle:active {
padding-bottom: 0px;
}
.input-source-switcher-symbol {
font-size: 34pt;
width: 96px;
height: 96px;
}
/* Background menu */
.background-menu {
-boxpointer-gap: 4px;
}

109
data/theme/more-results.svg Normal file
View File

@ -0,0 +1,109 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
id="svg12430"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="more-results.svg">
<defs
id="defs12432" />
<sodipodi:namedview
id="base"
pagecolor="#7a7a7a"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="8.3155237"
inkscape:cy="0.89548874"
inkscape:document-units="px"
inkscape:current-layer="g14642-3-0"
showgrid="false"
borderlayer="true"
inkscape:showpageshadow="false"
inkscape:window-width="2560"
inkscape:window-height="1376"
inkscape:window-x="1200"
inkscape:window-y="187"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid13002" />
</sodipodi:namedview>
<metadata
id="metadata12435">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1036.3622)">
<g
style="display:inline"
transform="translate(-141.99984,638.37113)"
inkscape:label="zoom-in"
id="g14642-3-0">
<path
sodipodi:type="inkscape:offset"
inkscape:radius="0"
inkscape:original="M 145.1875 400 C 144.5248 400 144 400.54899 144 401.21875 L 144 410.78125 C 144 411.45101 144.5248 412 145.1875 412 L 154.8125 412 C 155.4752 412 156 411.45101 156 410.78125 L 156 401.21875 C 156 400.54899 155.4752 400 154.8125 400 L 145.1875 400 z M 149 403 L 151 403 L 151 405 L 153 405 L 153 407 L 151 407 L 151 409 L 149 409 L 149 407 L 147 407 L 147 405 L 149 405 L 149 403 z "
xlink:href="#rect11749-5-0-1-8"
style="color:#bebebe;fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;opacity:0.8"
id="path13004"
inkscape:href="#rect11749-5-0-1-8"
d="M 145.1875,400 C 144.5248,400 144,400.54899 144,401.21875 l 0,9.5625 c 0,0.66976 0.5248,1.21875 1.1875,1.21875 l 9.625,0 c 0.6627,0 1.1875,-0.54899 1.1875,-1.21875 l 0,-9.5625 C 156,400.54899 155.4752,400 154.8125,400 L 145.1875,400 z m 3.8125,3 2,0 0,2 2,0 0,2 -2,0 0,2 -2,0 0,-2 -2,0 0,-2 2,0 L 149,403 Z"
transform="translate(0,1)" />
<use
x="0"
y="0"
xlink:href="#path13004"
id="use11960"
transform="translate(1,-1)"
width="16"
height="16" />
<use
x="0"
y="0"
xlink:href="#use11960"
id="use11962"
transform="translate(-2,0)"
width="16"
height="16" />
<path
inkscape:connector-curvature="0"
style="color:#bebebe;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible"
d="M 145.1875,400 C 144.5248,400 144,400.54899 144,401.21875 l 0,9.5625 c 0,0.66976 0.5248,1.21875 1.1875,1.21875 l 9.625,0 c 0.6627,0 1.1875,-0.54899 1.1875,-1.21875 l 0,-9.5625 C 156,400.54899 155.4752,400 154.8125,400 L 145.1875,400 z m 3.8125,3 2,0 0,2 2,0 0,2 -2,0 0,2 -2,0 0,-2 -2,0 0,-2 2,0 L 149,403 Z"
id="rect11749-5-0-1-8" />
<rect
style="fill:none;stroke:none"
id="rect3620-5-4"
width="15.981825"
height="16"
x="142"
y="398"
rx="0"
ry="0" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

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

View File

@ -43,12 +43,13 @@
</chapter>
<chapter>
<title>Integration helpers and utilities</title>
<xi:include href="doc-gen-org.gnome.Shell.SearchProvider.xml"/>
<xi:include href="doc-gen-org.gnome.Shell.SearchProvider2.xml"/>
<xi:include href="xml/shell-global.xml"/>
<xi:include href="xml/shell-wm.xml"/>
<xi:include href="xml/shell-xfixes-cursor.xml"/>
<xi:include href="xml/shell-util.xml"/>
<xi:include href="xml/shell-mount-operation.xml"/>
<xi:include href="xml/shell-mobile-providers.xml"/>
<xi:include href="xml/shell-network-agent.xml"/>
<xi:include href="xml/shell-polkit-authentication-agent.xml"/>
<xi:include href="xml/shell-tp-client.xml"/>

View File

@ -52,13 +52,6 @@ its dependencies to build from tarballs.</description>
<gnome:userid>walters</gnome:userid>
</foaf:Person>
</maintainer>
<maintainer>
<foaf:Person>
<foaf:name>Dan Winship</foaf:name>
<foaf:mbox rdf:resource="mailto:danw@gnome.org" />
<gnome:userid>danw</gnome:userid>
</foaf:Person>
</maintainer>
<maintainer>
<foaf:Person>
<foaf:name>Marina Zhurakhinskaya</foaf:name>
@ -73,4 +66,11 @@ its dependencies to build from tarballs.</description>
<gnome:userid>fmuellner</gnome:userid>
</foaf:Person>
</maintainer>
<maintainer>
<foaf:Person>
<foaf:name>Ray Strode</foaf:name>
<foaf:mbox rdf:resource="mailto:halfline@gmail.com" />
<gnome:userid>halfline</gnome:userid>
</foaf:Person>
</maintainer>
</Project>

View File

@ -28,6 +28,7 @@ nobase_dist_js_DATA = \
misc/extensionUtils.js \
misc/fileUtils.js \
misc/gnomeSession.js \
misc/hash.js \
misc/history.js \
misc/jsParse.js \
misc/loginManager.js \
@ -38,6 +39,8 @@ nobase_dist_js_DATA = \
ui/altTab.js \
ui/appDisplay.js \
ui/appFavorites.js \
ui/backgroundMenu.js \
ui/background.js \
ui/boxpointer.js \
ui/calendar.js \
ui/checkBox.js \
@ -49,7 +52,6 @@ nobase_dist_js_DATA = \
ui/extensionSystem.js \
ui/extensionDownloader.js \
ui/environment.js \
ui/flashspot.js \
ui/ibusCandidatePopup.js\
ui/grabHelper.js \
ui/iconGrid.js \
@ -62,17 +64,21 @@ nobase_dist_js_DATA = \
ui/main.js \
ui/messageTray.js \
ui/modalDialog.js \
ui/separator.js \
ui/sessionMode.js \
ui/shellEntry.js \
ui/shellMountOperation.js \
ui/notificationDaemon.js \
ui/osdWindow.js \
ui/overview.js \
ui/overviewControls.js \
ui/panel.js \
ui/panelMenu.js \
ui/pointerWatcher.js \
ui/popupMenu.js \
ui/remoteSearch.js \
ui/runDialog.js \
ui/screenshot.js \
ui/screenShield.js \
ui/scripting.js \
ui/search.js \
@ -85,9 +91,11 @@ nobase_dist_js_DATA = \
ui/status/power.js \
ui/status/volume.js \
ui/status/bluetooth.js \
ui/switcherPopup.js \
ui/tweener.js \
ui/unlockDialog.js \
ui/userMenu.js \
ui/userWidget.js \
ui/viewSelector.js \
ui/wanda.js \
ui/windowAttentionHandler.js \

View File

@ -45,6 +45,7 @@ const Application = new Lang.Class({
this._extensionPrefsModules = {};
this._extensionIters = {};
this._startupUuid = null;
},
_buildModel: function() {
@ -203,6 +204,7 @@ const Application = new Lang.Class({
_scanExtensions: function() {
let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', Lang.bind(this, this._extensionFound));
finder.connect('extensions-loaded', Lang.bind(this, this._extensionsLoaded));
finder.scanExtensions();
},
@ -212,6 +214,11 @@ const Application = new Lang.Class({
this._extensionIters[extension.uuid] = iter;
},
_extensionsLoaded: function() {
if (this._startupUuid && this._extensionAvailable(this._startupUuid))
this._selectExtension(this._startupUuid);
this._startupUuid = null;
},
_onActivate: function() {
this._window.present();
@ -232,10 +239,10 @@ const Application = new Lang.Class({
// Strip off "extension:///" prefix which fakes a URI, if it exists
uuid = stripPrefix(uuid, "extension:///");
if (!this._extensionAvailable(uuid))
return 1;
this._selectExtension(uuid);
if (this._extensionAvailable(uuid))
this._selectExtension(uuid);
else
this._startupUuid = uuid;
}
return 0;
}

View File

@ -39,50 +39,23 @@ const GdmUtil = imports.gdm.util;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const Panel = imports.ui.panel;
const PanelMenu = imports.ui.panelMenu;
const Tweener = imports.ui.tweener;
const UserMenu = imports.ui.userMenu;
const UserWidget = imports.ui.userWidget;
const _RESIZE_ANIMATION_TIME = 0.25;
const _FADE_ANIMATION_TIME = 0.25;
const _SCROLL_ANIMATION_TIME = 0.5;
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
const _LOGO_ICON_HEIGHT = 16;
const WORK_SPINNER_ICON_SIZE = 24;
const WORK_SPINNER_ANIMATION_DELAY = 1.0;
const WORK_SPINNER_ANIMATION_TIME = 0.3;
let _loginDialog = null;
function _smoothlyResizeActor(actor, width, height) {
let finalWidth;
let finalHeight;
if (width < 0)
finalWidth = actor.width;
else
finalWidth = width;
if (height < 0)
finalHeight = actor.height;
else
finalHeight = height;
actor.set_size(actor.width, actor.height);
if (actor.width == finalWidth && actor.height == finalHeight)
return null;
let hold = new Batch.Hold();
Tweener.addTween(actor,
{ width: finalWidth,
height: finalHeight,
time: _RESIZE_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
hold.release();
})
});
return hold;
}
const LogoMenuButton = new Lang.Class({
Name: 'LogoMenuButton',
Extends: PanelMenu.Button,
@ -123,6 +96,7 @@ const UserListItem = new Lang.Class({
let layout = new St.BoxLayout({ vertical: false });
this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true,
child: layout,
reactive: true,
@ -240,155 +214,23 @@ const UserList = new Lang.Class({
}
},
_showItem: function(item) {
let tasks = [function() {
return GdmUtil.fadeInActor(item.actor);
}];
let batch = new Batch.ConsecutiveBatch(this, tasks);
return batch.run();
},
_onItemActivated: function(activatedItem) {
this.emit('activate', activatedItem);
},
giveUpWhitespace: function() {
let container = this.actor.get_parent();
container.child_set(this.actor, { expand: false });
},
takeOverWhitespace: function() {
let container = this.actor.get_parent();
container.child_set(this.actor, { expand: true });
},
pinInPlace: function() {
this._box.set_size(this._box.width, this._box.height);
},
shrinkToNaturalHeight: function() {
let oldWidth = this._box.width;
let oldHeight = this._box.height;
this._box.set_size(-1, -1);
let [minHeight, naturalHeight] = this._box.get_preferred_height(-1);
this._box.set_size(oldWidth, oldHeight);
let batch = new Batch.ConsecutiveBatch(this,
[function() {
return _smoothlyResizeActor(this._box, -1, naturalHeight);
},
function() {
this._box.set_size(-1, -1);
}
]);
return batch.run();
},
hideItemsExcept: function(exception) {
updateStyle: function(isExpanded) {
let tasks = [];
for (let userName in this._items) {
let item = this._items[userName];
item.actor.set_hover(false);
item.actor.reactive = false;
item.actor.can_focus = false;
item.syncStyleClasses();
item._timedLoginIndicator.scale_x = 0.;
if (item != exception)
tasks.push(function() {
return GdmUtil.fadeOutActor(item.actor);
});
}
let batch = new Batch.ConsecutiveBatch(this,
[function() {
return GdmUtil.fadeOutActor(this.actor.vscroll);
},
new Batch.ConcurrentBatch(this, tasks),
function() {
this._box.remove_style_pseudo_class('expanded');
}
]);
return batch.run();
},
hideItems: function() {
return this.hideItemsExcept(null);
},
_getExpandedHeight: function() {
let hiddenActors = [];
for (let userName in this._items) {
let item = this._items[userName];
if (!item.actor.visible) {
item.actor.show();
hiddenActors.push(item.actor);
}
}
if (!this._box.visible) {
this._box.show();
hiddenActors.push(this._box);
}
this._box.set_size(-1, -1);
let [minHeight, naturalHeight] = this._box.get_preferred_height(-1);
for (let i = 0; i < hiddenActors.length; i++) {
let actor = hiddenActors[i];
actor.hide();
}
return naturalHeight;
},
showItems: function() {
let tasks = [];
if (isExpanded)
this._box.add_style_pseudo_class('expanded');
else
this._box.remove_style_pseudo_class('expanded');
for (let userName in this._items) {
let item = this._items[userName];
item.actor.sync_hover();
item.actor.reactive = true;
item.actor.can_focus = true;
item.syncStyleClasses();
tasks.push(function() {
return this._showItem(item);
});
}
let batch = new Batch.ConsecutiveBatch(this,
[function() {
this.takeOverWhitespace();
},
function() {
let fullHeight = this._getExpandedHeight();
return _smoothlyResizeActor(this._box, -1, fullHeight);
},
function() {
this._box.add_style_pseudo_class('expanded');
},
new Batch.ConcurrentBatch(this, tasks),
function() {
this.actor.set_size(-1, -1);
},
function() {
return GdmUtil.fadeInActor(this.actor.vscroll);
}]);
return batch.run();
},
scrollToItem: function(item) {
@ -487,6 +329,7 @@ const SessionListItem = new Lang.Class({
this.id = id;
this.actor = new St.Button({ style_class: 'login-dialog-session-list-item',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true,
reactive: true,
x_fill: true,
@ -527,6 +370,7 @@ const SessionListItem = new Lang.Class({
color.alpha / 255);
cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI);
cr.fill();
cr.$dispose();
},
_onClicked: function() {
@ -546,6 +390,7 @@ const SessionList = new Lang.Class({
this.actor.child = this._box;
this._button = new St.Button({ style_class: 'login-dialog-session-list-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true,
x_fill: true,
y_fill: true });
@ -557,7 +402,7 @@ const SessionList = new Lang.Class({
box.add_actor(this._triangle);
let label = new St.Label({ style_class: 'login-dialog-session-list-label',
text: _("Session...") });
text: _("Session") });
box.add_actor(label);
this._button.connect('clicked',
@ -604,6 +449,14 @@ const SessionList = new Lang.Class({
this.close();
},
updateSensitivity: function(sensitive) {
this._button.reactive = sensitive;
this._button.can_focus = sensitive;
for (let id in this._items)
this._items[id].actor.reactive = sensitive;
},
setActiveSession: function(sessionId) {
if (sessionId == this._activeSessionId)
return;
@ -660,6 +513,7 @@ const LoginDialog = new Lang.Class({
this.parent({ shellReactive: true,
styleClass: 'login-dialog',
parentActor: parentActor,
keybindingMode: Shell.KeyBindingMode.LOGIN_SCREEN,
shouldFadeIn: false });
this.connect('destroy',
Lang.bind(this, this._onDestroy));
@ -684,9 +538,11 @@ const LoginDialog = new Lang.Class({
this._userVerifier = new GdmUtil.ShellUserVerifier(this._greeterClient);
this._userVerifier.connect('ask-question', Lang.bind(this, this._askQuestion));
this._userVerifier.connect('show-message', Lang.bind(this, this._showMessage));
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
this._userVerifier.connect('verification-failed', Lang.bind(this, this._verificationFailed));
this._userVerifier.connect('reset', Lang.bind(this, this._reset));
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint));
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint));
this._verifyingUser = false;
this._settings = new Gio.Settings({ schema: GdmUtil.LOGIN_SCREEN_SCHEMA });
@ -694,24 +550,23 @@ const LoginDialog = new Lang.Class({
Lang.bind(this, this._updateBanner));
this._settings.connect('changed::' + GdmUtil.BANNER_MESSAGE_TEXT_KEY,
Lang.bind(this, this._updateBanner));
this._settings.connect('changed::' + GdmUtil.DISABLE_USER_LIST_KEY,
Lang.bind(this, this._updateDisableUserList));
this._userSelectionBox = new St.BoxLayout({ style_class: 'login-dialog-user-selection-box',
vertical: true });
this.contentLayout.add(this._userSelectionBox);
this._bannerLabel = new St.Label({ style_class: 'login-dialog-banner',
text: '' });
this.contentLayout.add(this._bannerLabel);
this._userSelectionBox.add(this._bannerLabel);
this._updateBanner();
this._titleLabel = new St.Label({ style_class: 'login-dialog-title',
text: C_("title", "Sign In") });
this.contentLayout.add(this._titleLabel,
{ y_fill: false,
y_align: St.Align.START });
this._userList = new UserList();
this.contentLayout.add(this._userList.actor,
{ expand: true,
x_fill: true,
y_fill: true });
this._userSelectionBox.add(this._userList.actor,
{ expand: true,
x_fill: true,
y_fill: true });
this.setInitialKeyFocus(this._userList.actor);
@ -722,6 +577,13 @@ const LoginDialog = new Lang.Class({
x_fill: true,
y_fill: true,
x_align: St.Align.START });
this._promptUser = new St.Bin({ x_fill: true,
x_align: St.Align.START });
this._promptBox.add(this._promptUser,
{ x_align: St.Align.START,
x_fill: true,
y_fill: true,
expand: true });
this._promptLabel = new St.Label({ style_class: 'login-dialog-prompt-label' });
this._promptBox.add(this._promptLabel,
@ -731,6 +593,7 @@ const LoginDialog = new Lang.Class({
x_align: St.Align.START });
this._promptEntry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
can_focus: true });
this._promptEntryTextChangedId = 0;
this._promptBox.add(this._promptEntry,
{ expand: true,
x_fill: true,
@ -744,6 +607,9 @@ const LoginDialog = new Lang.Class({
this._promptLoginHint.hide();
this._promptBox.add(this._promptLoginHint);
this._signInButton = null;
this._workSpinner = null;
this._sessionList = new SessionList();
this._sessionList.connect('session-activated',
Lang.bind(this, function(list, sessionId) {
@ -763,18 +629,19 @@ const LoginDialog = new Lang.Class({
let notListedLabel = new St.Label({ text: _("Not listed?"),
style_class: 'login-dialog-not-listed-label' });
this._notListedButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true,
child: notListedLabel,
reactive: true,
x_align: St.Align.START,
x_fill: true });
this._notListedButton.connect('clicked', Lang.bind(this, this._onNotListedClicked));
this._notListedButton.connect('clicked', Lang.bind(this, this._hideUserListAndLogIn));
this.contentLayout.add(this._notListedButton,
{ expand: false,
x_align: St.Align.START,
x_fill: true });
this._userSelectionBox.add(this._notListedButton,
{ expand: false,
x_align: St.Align.START,
x_fill: true });
if (!this._userManager.is_loaded)
this._userManagerLoadedId = this._userManager.connect('notify::is-loaded',
@ -795,43 +662,50 @@ const LoginDialog = new Lang.Class({
},
_updateDisableUserList: function() {
let disableUserList = this._settings.get_boolean(GdmUtil.DISABLE_USER_LIST_KEY);
// If this is the first time around, set initial focus
if (this._disableUserList == undefined && disableUserList)
this.setInitialKeyFocus(this._promptEntry);
if (disableUserList != this._disableUserList) {
this._disableUserList = disableUserList;
if (!this._verifyingUser)
this._reset();
}
},
_updateBanner: function() {
let enabled = this._settings.get_boolean(GdmUtil.BANNER_MESSAGE_KEY);
let text = this._settings.get_string(GdmUtil.BANNER_MESSAGE_TEXT_KEY);
if (enabled && text) {
this._bannerLabel.set_text(text);
this._fadeInBanner();
this._bannerLabel.show();
} else {
this._fadeOutBanner();
this._bannerLabel.hide();
}
},
_onReset: function(client, serviceName) {
_reset: function() {
this._updateSensitivity(true);
this._promptMessage.hide();
let tasks = [this._hidePrompt,
new Batch.ConcurrentBatch(this, [this._fadeInTitleLabel,
this._fadeInNotListedButton]),
function() {
this._sessionList.close();
this._promptLoginHint.hide();
this._userList.actor.show();
this._userList.actor.opacity = 255;
return this._userList.showItems();
},
function() {
this._userList.actor.reactive = true;
this._userList.actor.grab_key_focus();
}];
this._user = null;
this._verifyingUser = false;
let batch = new Batch.ConsecutiveBatch(this, tasks);
batch.run();
if (this._disableUserList)
this._hideUserListAndLogIn();
else
this._showUserList();
},
_verificationFailed: function() {
this._promptEntry.text = '';
this._updateSensitivity(true);
this._setWorking(false);
},
_onDefaultSessionChanged: function(client, sessionId) {
@ -842,80 +716,51 @@ const LoginDialog = new Lang.Class({
if (message) {
this._promptMessage.text = message;
this._promptMessage.styleClass = styleClass;
GdmUtil.fadeInActor(this._promptMessage);
this._promptMessage.show();
} else {
GdmUtil.fadeOutActor(this._promptMessage);
this._promptMessage.hide();
}
},
_showLoginHint: function(verifier, message) {
this._promptLoginHint.set_text(message)
GdmUtil.fadeInActor(this._promptLoginHint);
this._promptLoginHint.show();
this._promptLoginHint.opacity = 255;
},
_hideLoginHint: function() {
GdmUtil.fadeOutActor(this._promptLoginHint);
this._promptLoginHint.hide();
this._promptLoginHint.set_text('');
},
cancel: function() {
this._userVerifier.cancel();
if (this._verifyingUser)
this._userVerifier.cancel();
else
this._reset();
},
_fadeInPrompt: function() {
let tasks = [function() {
return GdmUtil.fadeInActor(this._promptLabel);
},
function() {
return GdmUtil.fadeInActor(this._promptEntry);
},
function() {
// Show it with 0 opacity so we preallocate space for it
// in the event we need to fade in the message
this._promptLoginHint.opacity = 0;
this._promptLoginHint.show();
},
function() {
return GdmUtil.fadeInActor(this._promptBox);
},
function() {
if (this._user && this._user.is_logged_in())
return null;
return GdmUtil.fadeInActor(this._sessionList.actor);
},
function() {
this._promptEntry.grab_key_focus();
}];
_showPrompt: function(forSecret) {
this._sessionList.actor.hide();
let batch = new Batch.ConcurrentBatch(this, tasks);
return batch.run();
},
this._promptLabel.show();
this._promptEntry.show();
this._promptLoginHint.opacity = 0;
this._promptLoginHint.show();
this._promptBox.opacity = 0;
this._promptBox.show();
Tweener.addTween(this._promptBox,
{ opacity: 255,
time: _FADE_ANIMATION_TIME,
transition: 'easeOutQuad' });
if ((this._user && !this._user.is_logged_in()) || this._verifyingUser)
this._sessionList.actor.show();
this._promptEntry.grab_key_focus();
_showPrompt: function() {
let hold = new Batch.Hold();
let buttons = [{ action: Lang.bind(this, this.cancel),
label: _("Cancel"),
key: Clutter.Escape },
{ action: Lang.bind(this, function() {
hold.release();
}),
label: C_("button", "Sign In"),
default: true }];
let tasks = [function() {
return this._fadeInPrompt();
},
function() {
this.setButtons(buttons);
this._prepareDialog(forSecret, hold);
},
hold];
@ -925,36 +770,147 @@ const LoginDialog = new Lang.Class({
return batch.run();
},
_prepareDialog: function(forSecret, hold) {
this._workSpinner = new Panel.AnimatedIcon('process-working.svg', WORK_SPINNER_ICON_SIZE);
this._workSpinner.actor.opacity = 0;
this._workSpinner.actor.show();
this.buttonLayout.visible = true;
this.clearButtons();
if (!this._disableUserList || this._verifyingUser)
this.addButton({ action: Lang.bind(this, this.cancel),
label: _("Cancel"),
key: Clutter.Escape },
{ expand: true,
x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
this.buttonLayout.add(this._workSpinner.actor,
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
this._signInButton = this.addButton({ action: Lang.bind(this, function() {
hold.release();
}),
label: forSecret ? C_("button", "Sign In") : _("Next"),
default: true },
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
this._updateSignInButtonSensitivity(this._promptEntry.text.length > 0);
this._promptEntryTextChangedId =
this._promptEntry.clutter_text.connect('text-changed',
Lang.bind(this, function() {
this._updateSignInButtonSensitivity(this._promptEntry.text.length > 0);
}));
},
_updateSensitivity: function(sensitive) {
this._promptEntry.reactive = sensitive;
this._promptEntry.clutter_text.editable = sensitive;
this._sessionList.updateSensitivity(sensitive);
this._updateSignInButtonSensitivity(sensitive);
},
_updateSignInButtonSensitivity: function(sensitive) {
if (this._signInButton) {
this._signInButton.reactive = sensitive;
this._signInButton.can_focus = sensitive;
}
},
_hidePrompt: function() {
this.setButtons([]);
let tasks = [function() {
return GdmUtil.fadeOutActor(this._promptBox);
},
if (this._promptEntryTextChangedId > 0) {
this._promptEntry.clutter_text.disconnect(this._promptEntryTextChangedId);
this._promptEntryTextChangedId = 0;
}
function() {
this._promptLoginHint.hide();
this._promptEntry.reactive = true;
this._promptEntry.set_text('');
}];
this._setWorking(false);
this._promptBox.hide();
this._promptLoginHint.hide();
let batch = new Batch.ConsecutiveBatch(this, tasks);
this._promptUser.set_child(null);
return batch.run();
this._updateSensitivity(true);
this._promptEntry.set_text('');
this._sessionList.close();
this._promptLoginHint.hide();
this.clearButtons();
this._workSpinner = null;
this._signInButton = null;
},
_setWorking: function(working) {
if (!this._workSpinner)
return;
if (working) {
this._workSpinner.play();
Tweener.addTween(this._workSpinner.actor,
{ opacity: 255,
delay: WORK_SPINNER_ANIMATION_DELAY,
time: WORK_SPINNER_ANIMATION_TIME,
transition: 'linear'
});
} else {
Tweener.addTween(this._workSpinner.actor,
{ opacity: 0,
time: WORK_SPINNER_ANIMATION_TIME,
transition: 'linear',
onCompleteScope: this,
onComplete: function() {
if (this._workSpinner)
this._workSpinner.stop();
}
});
}
},
_askQuestion: function(verifier, serviceName, question, passwordChar) {
this._promptLabel.set_text(question);
this._updateSensitivity(true);
this._promptEntry.set_text('');
this._promptEntry.clutter_text.set_password_char(passwordChar);
let tasks = [function() {
return this._showPrompt(!!passwordChar);
},
function() {
let text = this._promptEntry.get_text();
this._updateSensitivity(false);
this._setWorking(true);
this._userVerifier.answerQuery(serviceName, text);
}];
let batch = new Batch.ConsecutiveBatch(this, tasks);
return batch.run();
},
_askForUsernameAndLogIn: function() {
this._promptLabel.set_text(_("Username: "));
this._promptEntry.set_text('');
this._promptEntry.clutter_text.set_password_char('');
let tasks = [this._showPrompt,
function() {
let _text = this._promptEntry.get_text();
let userName = this._promptEntry.get_text();
this._promptEntry.reactive = false;
this._userVerifier.answerQuery(serviceName, _text);
return this._beginVerificationForUser(userName);
}];
let batch = new Batch.ConsecutiveBatch(this, tasks);
@ -962,7 +918,26 @@ const LoginDialog = new Lang.Class({
},
_onSessionOpened: function(client, serviceName) {
this._greeter.call_start_session_when_ready_sync(serviceName, true, null);
Tweener.addTween(this.dialogLayout,
{ opacity: 0,
time: _FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onUpdate: function() {
let children = Main.layoutManager.uiGroup.get_children();
for (let i = 0; i < children.length; i++) {
if (children[i] != Main.layoutManager.screenShieldGroup)
children[i].opacity = this.dialogLayout.opacity;
}
},
onUpdateScope: this,
onComplete: function() {
Mainloop.idle_add(Lang.bind(this, function() {
this._greeter.call_start_session_when_ready_sync(serviceName, true, null);
return false;
}));
},
onCompleteScope: this });
},
_waitForItemForUser: function(userName) {
@ -1089,96 +1064,55 @@ const LoginDialog = new Lang.Class({
}));
},
_onNotListedClicked: function(user) {
let tasks = [function() {
return this._userList.hideItems();
},
function() {
return this._userList.giveUpWhitespace();
},
function() {
this._userList.actor.hide();
},
new Batch.ConcurrentBatch(this, [this._fadeOutTitleLabel,
this._fadeOutNotListedButton]),
function() {
let hold = new Batch.Hold();
this._userVerifier.begin(null, hold);
return hold;
}];
let batch = new Batch.ConsecutiveBatch(this, tasks);
batch.run();
_setUserListExpanded: function(expanded) {
this._userList.updateStyle(expanded);
this._userSelectionBox.visible = expanded;
},
_fadeInBanner: function() {
return GdmUtil.fadeInActor(this._bannerLabel);
_hideUserListAndLogIn: function() {
this._setUserListExpanded(false);
GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
this._askForUsernameAndLogIn();
},
_fadeOutBanner: function() {
return GdmUtil.fadeOutActor(this._bannerLabel);
},
_fadeInTitleLabel: function() {
return GdmUtil.fadeInActor(this._titleLabel);
},
_fadeOutTitleLabel: function() {
return GdmUtil.fadeOutActor(this._titleLabel);
},
_fadeInNotListedButton: function() {
return GdmUtil.fadeInActor(this._notListedButton);
},
_fadeOutNotListedButton: function() {
return GdmUtil.fadeOutActor(this._notListedButton);
_showUserList: function() {
this._hidePrompt();
this._setUserListExpanded(true);
this._userList.actor.grab_key_focus();
},
_beginVerificationForUser: function(userName) {
let hold = new Batch.Hold();
this._userVerifier.begin(userName, hold);
this._verifyingUser = true;
return hold;
},
_onUserListActivated: function(activatedItem) {
let userName;
_beginVerificationForItem: function(item) {
let userWidget = new UserWidget.UserWidget(item.user);
this._promptUser.set_child(userWidget.actor);
let tasks = [function() {
this._userList.actor.reactive = false;
return this._userList.pinInPlace();
},
function() {
return this._userList.hideItemsExcept(activatedItem);
},
function() {
return this._userList.giveUpWhitespace();
},
new Batch.ConcurrentBatch(this, [this._fadeOutTitleLabel,
this._fadeOutNotListedButton]),
function() {
return this._userList.shrinkToNaturalHeight();
},
function() {
userName = activatedItem.user.get_user_name();
let userName = item.user.get_user_name();
return this._beginVerificationForUser(userName);
}];
let batch = new Batch.ConsecutiveBatch(this, tasks);
return batch.run();
},
_onUserListActivated: function(activatedItem) {
let tasks = [function() {
return GdmUtil.cloneAndFadeOutActor(this._userSelectionBox);
},
function() {
this._setUserListExpanded(false);
}];
this._user = activatedItem.user;
let batch = new Batch.ConsecutiveBatch(this, tasks);
let batch = new Batch.ConcurrentBatch(this, [new Batch.ConsecutiveBatch(this, tasks),
this._beginVerificationForItem(activatedItem)]);
batch.run();
},
@ -1196,6 +1130,8 @@ const LoginDialog = new Lang.Class({
this._userList.addUser(users[i]);
}
this._updateDisableUserList();
this._userManager.connect('user-added',
Lang.bind(this, function(userManager, user) {
this._userList.addUser(user);
@ -1219,7 +1155,7 @@ const LoginDialog = new Lang.Class({
_onOpened: function() {
Main.ctrlAltTabManager.addGroup(this.dialogLayout,
_("Login Window"),
'dialog-password',
'dialog-password-symbolic',
{ sortGroup: CtrlAltTab.SortGroup.MIDDLE });
},
@ -1227,6 +1163,6 @@ const LoginDialog = new Lang.Class({
close: function() {
this.parent();
Main.ctrlAltTabManager.removeGroup(this._group);
Main.ctrlAltTabManager.removeGroup(this.dialogLayout);
}
});

View File

@ -1,5 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
@ -15,6 +16,7 @@ const Tweener = imports.ui.tweener;
const PASSWORD_SERVICE_NAME = 'gdm-password';
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
const FADE_ANIMATION_TIME = 0.16;
const CLONE_FADE_ANIMATION_TIME = 0.25;
const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen';
const FINGERPRINT_AUTHENTICATION_KEY = 'enable-fingerprint-authentication';
@ -23,6 +25,7 @@ const BANNER_MESSAGE_TEXT_KEY = 'banner-message-text';
const ALLOWED_FAILURES_KEY = 'allowed-failures';
const LOGO_KEY = 'logo';
const DISABLE_USER_LIST_KEY = 'disable-user-list';
function fadeInActor(actor) {
if (actor.opacity == 255 && actor.visible)
@ -70,6 +73,34 @@ function fadeOutActor(actor) {
return hold;
}
function cloneAndFadeOutActor(actor) {
// Immediately hide actor so its sibling can have its space
// and position, but leave a non-reactive clone on-screen,
// so from the user's point of view it smoothly fades away
// and reveals its sibling.
actor.hide();
let clone = new Clutter.Clone({ source: actor,
reactive: false });
Main.uiGroup.add_child(clone);
let [x, y] = actor.get_transformed_position();
clone.set_position(x, y);
let hold = new Batch.Hold();
Tweener.addTween(clone,
{ opacity: 0,
time: CLONE_FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: function() {
clone.destroy();
hold.release();
}
});
return hold;
}
const ShellUserVerifier = new Lang.Class({
Name: 'ShellUserVerifier',

View File

@ -11,6 +11,7 @@ const Gio = imports.gi.Gio;
const ShellJS = imports.gi.ShellJS;
const Config = imports.misc.config;
const FileUtils = imports.misc.fileUtils;
const ExtensionType = {
SYSTEM: 1,
@ -150,53 +151,40 @@ function installImporter(extension) {
const ExtensionFinder = new Lang.Class({
Name: 'ExtensionFinder',
_scanExtensionsInDirectory: function(dir, type) {
let fileEnum;
let file, info;
try {
fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
} catch(e) {
if (e.domain != Gio.io_error_quark() || e.code != Gio.IOErrorEnum.NOT_FOUND)
logError(e, 'Could not enumerate extensions directory');
_loadExtension: function(extensionDir, info, perUserDir) {
let fileType = info.get_file_type();
if (fileType != Gio.FileType.DIRECTORY)
return;
let uuid = info.get_name();
let existing = extensions[uuid];
if (existing) {
log('Extension %s already installed in %s. %s will not be loaded'.format(uuid, existing.path, extensionDir.get_path()));
return;
}
while ((info = fileEnum.next_file(null)) != null) {
let fileType = info.get_file_type();
if (fileType != Gio.FileType.DIRECTORY)
continue;
let uuid = info.get_name();
let extensionDir = dir.get_child(uuid);
let existing = extensions[uuid];
if (existing) {
log('Extension %s already installed in %s. %s will not be loaded'.format(uuid, existing.path, extensionDir.get_path()));
continue;
}
let extension;
try {
extension = createExtensionObject(uuid, extensionDir, type);
} catch(e) {
logError(e, 'Could not load extension %s'.format(uuid));
continue;
}
this.emit('extension-found', extension);
let extension;
let type = extensionDir.has_prefix(perUserDir) ? ExtensionType.PER_USER
: ExtensionType.SYSTEM;
try {
extension = createExtensionObject(uuid, extensionDir, type);
} catch(e) {
logError(e, 'Could not load extension %s'.format(uuid));
return;
}
fileEnum.close(null);
this.emit('extension-found', extension);
},
_extensionsLoaded: function() {
this.emit('extensions-loaded');
},
scanExtensions: function() {
let userExtensionsDir = Gio.File.new_for_path(GLib.build_filenamev([global.userdatadir, 'extensions']));
this._scanExtensionsInDirectory(userExtensionsDir, ExtensionType.PER_USER);
let systemDataDirs = GLib.get_system_data_dirs();
for (let i = 0; i < systemDataDirs.length; i++) {
let dirPath = GLib.build_filenamev([systemDataDirs[i], 'gnome-shell', 'extensions']);
let dir = Gio.file_new_for_path(dirPath);
if (dir.query_exists(null))
this._scanExtensionsInDirectory(dir, ExtensionType.SYSTEM);
}
let perUserDir = Gio.File.new_for_path(global.userdatadir);
FileUtils.collectFromDatadirsAsync('extensions',
{ processFile: Lang.bind(this, this._loadExtension),
loadedCallback: Lang.bind(this, this._extensionsLoaded),
includeUserDir: true,
data: perUserDir });
}
});
Signals.addSignalMethods(ExtensionFinder.prototype);

View File

@ -2,10 +2,12 @@
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Params = imports.misc.params;
function listDirAsync(file, callback) {
let allFiles = [];
file.enumerate_children_async(Gio.FILE_ATTRIBUTE_STANDARD_NAME,
file.enumerate_children_async('standard::name,standard::type',
Gio.FileQueryInfoFlags.NONE,
GLib.PRIORITY_LOW, null, function (obj, res) {
let enumerator = obj.enumerate_children_finish(res);
@ -23,6 +25,63 @@ function listDirAsync(file, callback) {
});
}
function _collectFromDirectoryAsync(dir, loadState) {
function done() {
loadState.numLoading--;
if (loadState.loadedCallback &&
loadState.numLoading == 0)
loadState.loadedCallback(loadState.data);
}
dir.query_info_async('standard::type', Gio.FileQueryInfoFlags.NONE,
GLib.PRIORITY_DEFAULT, null, function(object, res) {
try {
object.query_info_finish(res);
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.NOT_FOUND))
log(e.message);
done();
return;
}
listDirAsync(dir, Lang.bind(this, function(infos) {
for (let i = 0; i < infos.length; i++)
loadState.processFile(dir.get_child(infos[i].get_name()),
infos[i], loadState.data);
done();
}));
});
}
function collectFromDatadirsAsync(subdir, params) {
params = Params.parse(params, { includeUserDir: false,
processFile: null,
loadedCallback: null,
data: null });
let loadState = { data: params.data,
numLoading: 0,
loadedCallback: params.loadedCallback,
processFile: params.processFile };
if (params.processFile == null) {
if (params.loadedCallback)
params.loadedCallback(params.data);
return;
}
let dataDirs = GLib.get_system_data_dirs();
if (params.includeUserDir)
dataDirs.unshift(GLib.get_user_data_dir());
loadState.numLoading = dataDirs.length;
for (let i = 0; i < dataDirs.length; i++) {
let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', subdir]);
let dir = Gio.File.new_for_path(path);
_collectFromDirectoryAsync(dir, loadState);
}
}
function deleteGFile(file) {
// Work around 'delete' being a keyword in JS.
return file['delete'](null);

View File

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

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

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

View File

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

View File

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

View File

@ -1,6 +1,8 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const St = imports.gi.St;
const Main = imports.ui.main;
@ -159,86 +161,6 @@ function killall(processName) {
}
}
// This was ported from network-manager-applet
// Copyright 2007 - 2011 Red Hat, Inc.
// Author: Dan Williams <dcbw@redhat.com>
const _IGNORED_WORDS = [
'Semiconductor',
'Components',
'Corporation',
'Communications',
'Company',
'Corp.',
'Corp',
'Co.',
'Inc.',
'Inc',
'Incorporated',
'Ltd.',
'Limited.',
'Intel',
'chipset',
'adapter',
'[hex]',
'NDIS',
'Module'
];
const _IGNORED_PHRASES = [
'Multiprotocol MAC/baseband processor',
'Wireless LAN Controller',
'Wireless LAN Adapter',
'Wireless Adapter',
'Network Connection',
'Wireless Cardbus Adapter',
'Wireless CardBus Adapter',
'54 Mbps Wireless PC Card',
'Wireless PC Card',
'Wireless PC',
'PC Card with XJACK(r) Antenna',
'Wireless cardbus',
'Wireless LAN PC Card',
'Technology Group Ltd.',
'Communication S.p.A.',
'Business Mobile Networks BV',
'Mobile Broadband Minicard Composite Device',
'Mobile Communications AB',
'(PC-Suite Mode)'
];
function fixupPCIDescription(desc) {
desc = desc.replace(/[_,]/, ' ');
/* Attempt to shorten ID by ignoring certain phrases */
for (let i = 0; i < _IGNORED_PHRASES.length; i++) {
let item = _IGNORED_PHRASES[i];
let pos = desc.indexOf(item);
if (pos != -1) {
let before = desc.substring(0, pos);
let after = desc.substring(pos + item.length, desc.length);
desc = before + after;
}
}
/* Attmept to shorten ID by ignoring certain individual words */
let words = desc.split(' ');
let out = [ ];
for (let i = 0; i < words.length; i++) {
let item = words[i];
// skip empty items (that come out from consecutive spaces)
if (item.length == 0)
continue;
if (_IGNORED_WORDS.indexOf(item) == -1) {
out.push(item);
}
}
return out.join(' ');
}
// lowerBound:
// @array: an array or array-like object, already sorted
// according to @cmp
@ -288,3 +210,27 @@ function insertSorted(array, val, cmp) {
return pos;
}
function makeCloseButton() {
let closeButton = new St.Button({ style_class: 'notification-close'});
// This is a bit tricky. St.Bin has its own x-align/y-align properties
// that compete with Clutter's properties. This should be fixed for
// Clutter 2.0. Since St.Bin doesn't define its own setters, the
// setters are a workaround to get Clutter's version.
closeButton.set_x_align(Clutter.ActorAlign.END);
closeButton.set_y_align(Clutter.ActorAlign.START);
// XXX Clutter 2.0 workaround: ClutterBinLayout needs expand
// to respect the alignments.
closeButton.set_x_expand(true);
closeButton.set_y_expand(true);
closeButton.connect('style-changed', function() {
let themeNode = closeButton.get_theme_node();
closeButton.translation_x = themeNode.get_length('-shell-close-overlap-x');
closeButton.translation_y = themeNode.get_length('-shell-close-overlap-y');
});
return closeButton;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,9 @@
// -*- 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 GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;
const GMenu = imports.gi.GMenu;
const Shell = imports.gi.Shell;
@ -13,12 +15,13 @@ const Mainloop = imports.mainloop;
const Atk = imports.gi.Atk;
const AppFavorites = imports.ui.appFavorites;
const BoxPointer = imports.ui.boxpointer;
const DND = imports.ui.dnd;
const IconGrid = imports.ui.iconGrid;
const Main = imports.ui.main;
const Overview = imports.ui.overview;
const OverviewControls = imports.ui.overviewControls;
const PopupMenu = imports.ui.popupMenu;
const Search = imports.ui.search;
const Tweener = imports.ui.tweener;
const Workspace = imports.ui.workspace;
const Params = imports.misc.params;
@ -27,60 +30,232 @@ const Util = imports.misc.util;
const MAX_APPLICATION_WORK_MILLIS = 75;
const MENU_POPUP_TIMEOUT = 600;
const SCROLL_TIME = 0.1;
const MAX_COLUMNS = 6;
const INACTIVE_GRID_OPACITY = 77;
const FOLDER_SUBICON_FRACTION = .4;
// Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
function _loadCategory(dir, view) {
let iter = dir.iter();
let appSystem = Shell.AppSystem.get_default();
let nextType;
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
if (nextType == GMenu.TreeItemType.ENTRY) {
let entry = iter.get_entry();
let app = appSystem.lookup_app_by_tree_entry(entry);
if (!entry.get_app_info().get_nodisplay())
view.addApp(app);
} else if (nextType == GMenu.TreeItemType.DIRECTORY) {
let itemDir = iter.get_directory();
if (!itemDir.get_is_nodisplay())
_loadCategory(itemDir, view);
}
}
};
const AlphabeticalView = new Lang.Class({
Name: 'AlphabeticalView',
Abstract: true,
_init: function() {
this._grid = new IconGrid.IconGrid({ xAlign: St.Align.START });
this._appSystem = Shell.AppSystem.get_default();
this._grid = new IconGrid.IconGrid({ xAlign: St.Align.MIDDLE,
columnLimit: MAX_COLUMNS });
this._pendingAppLaterId = 0;
this._appIcons = {}; // desktop file id
this._allApps = [];
// Standard hack for ClutterBinLayout
this._grid.actor.x_expand = true;
let box = new St.BoxLayout({ vertical: true });
box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
this.actor = new St.ScrollView({ x_fill: true,
y_fill: false,
y_align: St.Align.START,
style_class: 'vfade' });
this.actor.add_actor(box);
this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
this.actor.connect('notify::mapped', Lang.bind(this,
function() {
if (!this.actor.mapped)
return;
let adjustment = this.actor.vscroll.adjustment;
let direction = Overview.SwipeScrollDirection.VERTICAL;
Main.overview.setScrollAdjustment(adjustment, direction);
// Reset scroll on mapping
adjustment.value = 0;
}));
this._items = {};
this._allItems = [];
},
removeAll: function() {
this._grid.removeAll();
this._appIcons = {};
this._allApps = [];
this._items = {};
this._allItems = [];
},
_getItemId: function(item) {
throw new Error('Not implemented');
},
_createItemIcon: function(item) {
throw new Error('Not implemented');
},
_compareItems: function(a, b) {
throw new Error('Not implemented');
},
_addItem: function(item) {
let id = this._getItemId(item);
if (this._items[id] !== undefined)
return null;
let itemIcon = this._createItemIcon(item);
this._allItems.push(item);
this._items[id] = itemIcon;
return itemIcon;
},
loadGrid: function() {
this._allItems.sort(this._compareItems);
for (let i = 0; i < this._allItems.length; i++) {
let id = this._getItemId(this._allItems[i]);
if (!id)
continue;
this._grid.addItem(this._items[id].actor);
}
}
});
const FolderView = new Lang.Class({
Name: 'FolderView',
Extends: AlphabeticalView,
_init: function() {
this.parent();
this.actor = this._grid.actor;
},
_getItemId: function(item) {
return item.get_id();
},
_createItemIcon: function(item) {
return new AppIcon(item);
},
_compareItems: function(a, b) {
return a.compare_by_name(b);
},
addApp: function(app) {
var id = app.get_id();
if (this._appIcons[id] !== undefined)
return;
this._addItem(app);
},
let appIcon = new AppWellIcon(app);
let pos = Util.insertSorted(this._allApps, app, function(a, b) {
return a.compare_by_name(b);
});
this._grid.addItem(appIcon.actor, pos);
appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
createFolderIcon: function(size) {
let icon = new St.Widget({ layout_manager: new Clutter.BinLayout(),
style_class: 'app-folder-icon',
width: size, height: size });
let subSize = Math.floor(FOLDER_SUBICON_FRACTION * size);
this._appIcons[id] = appIcon;
let aligns = [ Clutter.ActorAlign.START, Clutter.ActorAlign.END ];
for (let i = 0; i < Math.min(this._allItems.length, 4); i++) {
let texture = this._allItems[i].create_icon_texture(subSize);
let bin = new St.Bin({ child: texture,
x_expand: true, y_expand: true });
bin.set_x_align(aligns[i % 2]);
bin.set_y_align(aligns[Math.floor(i / 2)]);
icon.add_actor(bin);
}
return icon;
}
});
const AllView = new Lang.Class({
Name: 'AllView',
Extends: AlphabeticalView,
_init: function() {
this.parent();
let box = new St.BoxLayout({ vertical: true });
this._stack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
this._stack.add_actor(this._grid.actor);
this._eventBlocker = new St.Widget({ x_expand: true, y_expand: true });
this._stack.add_actor(this._eventBlocker);
box.add(this._stack, { y_align: St.Align.START, expand: true });
this.actor = new St.ScrollView({ x_fill: true,
y_fill: false,
y_align: St.Align.START,
x_expand: true,
y_expand: true,
overlay_scrollbars: true,
style_class: 'all-apps vfade' });
this.actor.add_actor(box);
this.actor.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
let action = new Clutter.PanAction({ interpolate: true });
action.connect('pan', Lang.bind(this, this._onPan));
this.actor.add_action(action);
this._clickAction = new Clutter.ClickAction();
this._clickAction.connect('clicked', Lang.bind(this, function() {
if (!this._currentPopup)
return;
let [x, y] = this._clickAction.get_coords();
let actor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
if (!this._currentPopup.actor.contains(actor))
this._currentPopup.popdown();
}));
this._eventBlocker.add_action(this._clickAction);
},
_onPan: function(action) {
this._clickAction.release();
let [dist, dx, dy] = action.get_motion_delta(0);
let adjustment = this.actor.vscroll.adjustment;
adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
return false;
},
_getItemId: function(item) {
if (item instanceof Shell.App)
return item.get_id();
else if (item instanceof GMenu.TreeDirectory)
return item.get_menu_id();
else
return null;
},
_createItemIcon: function(item) {
if (item instanceof Shell.App)
return new AppIcon(item);
else if (item instanceof GMenu.TreeDirectory)
return new FolderIcon(item, this);
else
return null;
},
_compareItems: function(itemA, itemB) {
// bit of a hack: rely on both ShellApp and GMenuTreeDirectory
// having a get_name() method
let nameA = GLib.utf8_collate_key(itemA.get_name(), -1);
let nameB = GLib.utf8_collate_key(itemB.get_name(), -1);
return (nameA > nameB) ? 1 : (nameA < nameB ? -1 : 0);
},
addApp: function(app) {
let appIcon = this._addItem(app);
if (appIcon)
appIcon.actor.connect('key-focus-in',
Lang.bind(this, this._ensureIconVisible));
},
addFolder: function(dir) {
let folderIcon = this._addItem(dir);
if (folderIcon)
folderIcon.actor.connect('key-focus-in',
Lang.bind(this, this._ensureIconVisible));
},
addFolderPopup: function(popup) {
this._stack.add_actor(popup.actor);
popup.connect('open-state-changed', Lang.bind(this,
function(popup, isOpen) {
this._eventBlocker.reactive = isOpen;
this._currentPopup = isOpen ? popup : null;
this._updateIconOpacities(isOpen);
if (isOpen)
this._ensureIconVisible(popup.actor);
}));
},
_ensureIconVisible: function(icon) {
@ -88,9 +263,9 @@ const AlphabeticalView = new Lang.Class({
let [value, lower, upper, stepIncrement, pageIncrement, pageSize] = adjustment.get_values();
let offset = 0;
let vfade = this.actor.get_effect("vfade");
let vfade = this.actor.get_effect("fade");
if (vfade)
offset = vfade.fade_offset;
offset = vfade.vfade_offset;
// If this gets called as part of a right-click, the actor
// will be needs_allocation, and so "icon.y" would return 0
@ -109,165 +284,170 @@ const AlphabeticalView = new Lang.Class({
transition: 'easeOutQuad' });
},
setVisibleApps: function(apps) {
if (apps == null) { // null implies "all"
for (var id in this._appIcons) {
var icon = this._appIcons[id];
icon.actor.visible = true;
}
} else {
// Set everything to not-visible, then set to visible what we should see
for (var id in this._appIcons) {
var icon = this._appIcons[id];
icon.actor.visible = false;
}
for (var i = 0; i < apps.length; i++) {
var app = apps[i];
var id = app.get_id();
var icon = this._appIcons[id];
icon.actor.visible = true;
}
_updateIconOpacities: function(folderOpen) {
for (let id in this._items) {
if (folderOpen && !this._items[id].actor.checked)
this._items[id].actor.opacity = INACTIVE_GRID_OPACITY;
else
this._items[id].actor.opacity = 255;
}
}
});
const ViewByCategories = new Lang.Class({
Name: 'ViewByCategories',
const FrequentView = new Lang.Class({
Name: 'FrequentView',
_init: function() {
this._grid = new IconGrid.IconGrid({ xAlign: St.Align.MIDDLE,
fillParent: true,
columnLimit: MAX_COLUMNS });
this.actor = new St.Widget({ style_class: 'frequent-apps',
x_expand: true, y_expand: true });
this.actor.add_actor(this._grid.actor);
this._usage = Shell.AppUsage.get_default();
},
removeAll: function() {
this._grid.removeAll();
},
loadApps: function() {
let mostUsed = this._usage.get_most_used ("");
for (let i = 0; i < mostUsed.length; i++) {
let appIcon = new AppIcon(mostUsed[i]);
this._grid.addItem(appIcon.actor, -1);
}
}
});
const Views = {
FREQUENT: 0,
ALL: 1
};
const AppDisplay = new Lang.Class({
Name: 'AppDisplay',
_init: function() {
this._appSystem = Shell.AppSystem.get_default();
this.actor = new St.BoxLayout({ style_class: 'all-app' });
this.actor._delegate = this;
this._appSystem.connect('installed-changed', Lang.bind(this, function() {
Main.queueDeferredWork(this._allAppsWorkId);
}));
Main.overview.connect('showing', Lang.bind(this, function() {
Main.queueDeferredWork(this._frequentAppsWorkId);
}));
global.settings.connect('changed::app-folder-categories', Lang.bind(this, function() {
Main.queueDeferredWork(this._allAppsWorkId);
}));
this._view = new AlphabeticalView();
this._views = [];
// categories can be -1 (the All view) or 0...n-1, where n
// is the number of sections
// -2 is a flag to indicate that nothing is selected
// (used only before the actor is mapped the first time)
this._currentCategory = -2;
this._categories = [];
let view, button;
view = new FrequentView();
button = new St.Button({ label: _("Frequent"),
style_class: 'app-view-control',
can_focus: true,
x_expand: true });
this._views[Views.FREQUENT] = { 'view': view, 'control': button };
this._categoryBox = new St.BoxLayout({ vertical: true,
reactive: true,
accessible_role: Atk.Role.LIST });
this._categoryScroll = new St.ScrollView({ x_fill: false,
y_fill: false,
style_class: 'vfade' });
this._categoryScroll.add_actor(this._categoryBox);
this.actor.add(this._view.actor, { expand: true, x_fill: true, y_fill: true });
this.actor.add(this._categoryScroll, { expand: false, y_fill: false, y_align: St.Align.START });
view = new AllView();
button = new St.Button({ label: _("All"),
style_class: 'app-view-control',
can_focus: true,
x_expand: true });
this._views[Views.ALL] = { 'view': view, 'control': button };
// Always select the "All" filter when switching to the app view
this.actor.connect('notify::mapped', Lang.bind(this,
function() {
if (this.actor.mapped && this._allCategoryButton)
this._selectCategory(-1);
}));
this.actor = new St.BoxLayout({ style_class: 'app-display',
vertical: true,
x_expand: true, y_expand: true });
this._viewStack = new St.Widget({ layout_manager: new Clutter.BinLayout(),
x_expand: true, y_expand: true });
this.actor.add(this._viewStack, { expand: true });
let layout = new Clutter.BoxLayout({ homogeneous: true });
this._controls = new St.Widget({ style_class: 'app-view-controls',
layout_manager: layout });
this.actor.add(new St.Bin({ child: this._controls }));
for (let i = 0; i < this._views.length; i++) {
this._viewStack.add_actor(this._views[i].view.actor);
this._controls.add_actor(this._views[i].control);
let viewIndex = i;
this._views[i].control.connect('clicked', Lang.bind(this,
function(actor) {
this._showView(viewIndex);
}));
}
this._showView(Views.FREQUENT);
// We need a dummy actor to catch the keyboard focus if the
// user Ctrl-Alt-Tabs here before the deferred work creates
// our real contents
this._focusDummy = new St.Bin({ can_focus: true });
this.actor.add(this._focusDummy);
this._viewStack.add_actor(this._focusDummy);
this._allAppsWorkId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplayAllApps));
this._frequentAppsWorkId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplayFrequentApps));
},
_selectCategory: function(num) {
if (this._currentCategory == num) // nothing to do
return;
this._currentCategory = num;
if (num != -1) {
var category = this._categories[num];
this._allCategoryButton.remove_style_pseudo_class('selected');
this._view.setVisibleApps(category.apps);
} else {
this._allCategoryButton.add_style_pseudo_class('selected');
this._view.setVisibleApps(null);
}
for (var i = 0; i < this._categories.length; i++) {
if (i == num)
this._categories[i].button.add_style_pseudo_class('selected');
_showView: function(activeIndex) {
for (let i = 0; i < this._views.length; i++) {
let actor = this._views[i].view.actor;
let params = { time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME,
opacity: (i == activeIndex) ? 255 : 0 };
if (i == activeIndex)
actor.visible = true;
else
this._categories[i].button.remove_style_pseudo_class('selected');
params.onComplete = function() { actor.hide(); };
Tweener.addTween(actor, params);
if (i == activeIndex)
this._views[i].control.add_style_pseudo_class('checked');
else
this._views[i].control.remove_style_pseudo_class('checked');
}
},
// Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
_loadCategory: function(dir, appList) {
var iter = dir.iter();
var nextType;
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
if (nextType == GMenu.TreeItemType.ENTRY) {
var entry = iter.get_entry();
var app = this._appSystem.lookup_app_by_tree_entry(entry);
if (!entry.get_app_info().get_nodisplay()) {
this._view.addApp(app);
appList.push(app);
}
} else if (nextType == GMenu.TreeItemType.DIRECTORY) {
var itemDir = iter.get_directory();
if (!itemDir.get_is_nodisplay())
this._loadCategory(itemDir, appList);
}
}
_redisplay: function() {
this._redisplayFrequentApps();
this._redisplayAllApps();
},
_addCategory: function(name, index, dir) {
let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
style_class: 'app-filter',
x_align: St.Align.START,
can_focus: true ,
accessible_role: Atk.Role.LIST_ITEM });
button.connect('clicked', Lang.bind(this, function() {
this._selectCategory(index);
}));
_redisplayFrequentApps: function() {
let view = this._views[Views.FREQUENT].view;
var apps;
if (dir == null) {
this._allCategoryButton = button;
} else {
apps = [];
this._loadCategory(dir, apps);
this._categories.push({ apps: apps,
name: name,
button: button });
}
this._categoryBox.add(button, { expand: true, x_fill: true, y_fill: false });
view.removeAll();
view.loadApps();
},
_removeAll: function() {
this._view.removeAll();
this._categories = [];
this._categoryBox.destroy_all_children();
},
_redisplayAllApps: function() {
let view = this._views[Views.ALL].view;
refresh: function() {
this._removeAll();
view.removeAll();
/* Translators: Filter to display all applications */
this._addCategory(_("All"), -1, null);
let tree = this._appSystem.get_tree();
let root = tree.get_root_directory();
var tree = this._appSystem.get_tree();
var root = tree.get_root_directory();
var iter = root.iter();
var nextType;
var i = 0;
let iter = root.iter();
let nextType;
let folderCategories = global.settings.get_strv('app-folder-categories');
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
if (nextType == GMenu.TreeItemType.DIRECTORY) {
var dir = iter.get_directory();
let dir = iter.get_directory();
if (dir.get_is_nodisplay())
continue;
this._addCategory(dir.get_name(), i, dir);
i++;
if (folderCategories.indexOf(dir.get_menu_id()) != -1)
view.addFolder(dir);
else
_loadCategory(dir, view);
}
}
this._selectCategory(-1);
view.loadGrid();
if (this._focusDummy) {
let focused = this._focusDummy.has_key_focus();
@ -279,36 +459,12 @@ const ViewByCategories = new Lang.Class({
}
});
/* This class represents a display containing a collection of application items.
* The applications are sorted based on their name.
*/
const AllAppDisplay = new Lang.Class({
Name: 'AllAppDisplay',
_init: function() {
this._appSystem = Shell.AppSystem.get_default();
this._appSystem.connect('installed-changed', Lang.bind(this, function() {
Main.queueDeferredWork(this._workId);
}));
this._appView = new ViewByCategories();
this.actor = new St.Bin({ child: this._appView.actor, x_fill: true, y_fill: true });
this._workId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
},
_redisplay: function() {
this._appView.refresh();
}
});
const AppSearchProvider = new Lang.Class({
Name: 'AppSearchProvider',
Extends: Search.SearchProvider,
_init: function() {
this.parent(_("APPLICATIONS"));
this._appSys = Shell.AppSystem.get_default();
this.id = 'applications';
},
getResultMetas: function(apps, callback) {
@ -333,18 +489,15 @@ const AppSearchProvider = new Lang.Class({
this.searchSystem.pushResults(this, this._appSys.subsearch(previousResults, terms));
},
activateResult: function(app, params) {
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
activateResult: function(app) {
let event = Clutter.get_current_event();
let modifiers = event ? event.get_state() : 0;
let openNewWindow = modifiers & Clutter.ModifierType.CONTROL_MASK;
if (openNewWindow)
app.open_new_window(params.workspace);
app.open_new_window(-1);
else
app.activate_full(params.workspace, params.timestamp);
app.activate();
},
dragActivateResult: function(id, params) {
@ -357,83 +510,163 @@ const AppSearchProvider = new Lang.Class({
createResultActor: function (resultMeta, terms) {
let app = resultMeta['id'];
let icon = new AppWellIcon(app);
let icon = new AppIcon(app);
return icon.actor;
}
});
const SettingsSearchProvider = new Lang.Class({
Name: 'SettingsSearchProvider',
Extends: Search.SearchProvider,
const FolderIcon = new Lang.Class({
Name: 'FolderIcon',
_init: function() {
this.parent(_("SETTINGS"));
_init: function(dir, parentView) {
this._dir = dir;
this._parentView = parentView;
this._appSys = Shell.AppSystem.get_default();
this._gnomecc = this._appSys.lookup_app('gnome-control-center.desktop');
this.actor = new St.Button({ style_class: 'app-well-app app-folder',
button_mask: St.ButtonMask.ONE,
toggle_mode: true,
can_focus: true,
x_fill: true,
y_fill: true });
this.actor._delegate = this;
let label = this._dir.get_name();
this.icon = new IconGrid.BaseIcon(label,
{ createIcon: Lang.bind(this, this._createIcon) });
this.actor.set_child(this.icon.actor);
this.actor.label_actor = this.icon.label;
this.view = new FolderView();
this.view.actor.reactive = false;
_loadCategory(dir, this.view);
this.view.loadGrid();
this.actor.connect('clicked', Lang.bind(this,
function() {
this._ensurePopup();
this._popup.toggle();
}));
this.actor.connect('notify::mapped', Lang.bind(this,
function() {
if (!this.actor.mapped && this._popup)
this._popup.popdown();
}));
},
getResultMetas: function(prefs, callback) {
let metas = [];
for (let i = 0; i < prefs.length; i++) {
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 this.view.createFolderIcon(size);
},
_ensurePopup: function() {
if (this._popup)
return;
let spaceTop = this.actor.y;
let spaceBottom = this._parentView.actor.height - (this.actor.y + this.actor.height);
let side = spaceTop > spaceBottom ? St.Side.BOTTOM : St.Side.TOP;
this._popup = new AppFolderPopup(this, side);
this._parentView.addFolderPopup(this._popup);
// Position the popup above or below the source icon
if (side == St.Side.BOTTOM) {
this._popup.actor.show();
this._popup.actor.y = this.actor.y - this._popup.actor.height;
this._popup.actor.hide();
} else {
this._popup.actor.y = this.actor.y + this.actor.height;
}
callback(metas);
this._popup.connect('open-state-changed', Lang.bind(this,
function(popup, isOpen) {
if (!isOpen)
this.actor.checked = false;
}));
},
});
const AppFolderPopup = new Lang.Class({
Name: 'AppFolderPopup',
_init: function(source, side) {
this._source = source;
this._view = source.view;
this._arrowSide = side;
this._isOpen = false;
this.actor = new St.Widget({ layout_manager: new Clutter.BinLayout(),
visible: false,
// We don't want to expand really, but look
// at the layout manager of our parent...
//
// DOUBLE HACK: if you set one, you automatically
// get the effect for the other direction too, so
// we need to set the y_align
x_expand: true,
y_expand: true,
x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.START });
this._boxPointer = new BoxPointer.BoxPointer(this._arrowSide,
{ style_class: 'app-folder-popup-bin',
x_fill: true,
y_fill: true,
x_align: St.Align.START });
this._boxPointer.actor.style_class = 'app-folder-popup';
this.actor.add_actor(this._boxPointer.actor);
this._boxPointer.bin.set_child(this._view.actor);
let closeButton = Util.makeCloseButton();
closeButton.connect('clicked', Lang.bind(this, this.popdown));
this.actor.add_actor(closeButton);
this._boxPointer.actor.bind_property('opacity', closeButton, 'opacity',
GObject.BindingFlags.SYNC_CREATE);
source.actor.connect('destroy', Lang.bind(this,
function() {
this.actor.destroy();
}));
},
getInitialResultSet: function(terms) {
this.searchSystem.pushResults(this, this._appSys.search_settings(terms));
toggle: function() {
if (this._isOpen)
this.popdown();
else
this.popup();
},
getSubsearchResultSet: function(previousResults, terms) {
this.searchSystem.pushResults(this, this._appSys.search_settings(terms));
popup: function() {
if (this._isOpen)
return;
this.actor.show();
this._boxPointer.setArrowActor(this._source.actor);
this._boxPointer.show(BoxPointer.PopupAnimation.FADE |
BoxPointer.PopupAnimation.SLIDE);
this._isOpen = true;
this.emit('open-state-changed', true);
},
activateResult: function(pref, params) {
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
popdown: function() {
if (!this._isOpen)
return;
pref.activate_full(params.workspace, params.timestamp);
},
dragActivateResult: function(pref, params) {
this.activateResult(pref, params);
},
createResultActor: function (resultMeta, terms) {
let app = resultMeta['id'];
let icon = new AppWellIcon(app);
return icon.actor;
this._boxPointer.hide(BoxPointer.PopupAnimation.FADE |
BoxPointer.PopupAnimation.SLIDE);
this._isOpen = false;
this.emit('open-state-changed', false);
}
});
Signals.addSignalMethods(AppFolderPopup.prototype);
const AppIcon = new Lang.Class({
Name: 'AppIcon',
Extends: IconGrid.BaseIcon,
_init : function(app, params) {
this.app = app;
let label = this.app.get_name();
this.parent(label, params);
},
createIcon: function(iconSize) {
return this.app.create_icon_texture(iconSize);
}
});
const AppWellIcon = new Lang.Class({
Name: 'AppWellIcon',
_init : function(app, iconParams, onActivateOverride) {
_init : function(app, iconParams) {
this.app = app;
this.actor = new St.Button({ style_class: 'app-well-app',
reactive: true,
@ -443,13 +676,15 @@ const AppWellIcon = new Lang.Class({
y_fill: true });
this.actor._delegate = this;
this.icon = new AppIcon(app, iconParams);
if (!iconParams)
iconParams = {};
iconParams['createIcon'] = Lang.bind(this, this._createIcon);
this.icon = new IconGrid.BaseIcon(app.get_name(), iconParams);
this.actor.set_child(this.icon.actor);
this.actor.label_actor = this.icon.label;
// A function callback to override the default "app.activate()"; used by preferences
this._onActivateOverride = onActivateOverride;
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this.actor.connect('popup-menu', Lang.bind(this, this._onKeyboardPopupMenu));
@ -479,7 +714,6 @@ const AppWellIcon = new Lang.Class({
Lang.bind(this,
this._onStateChanged));
this._onStateChanged();
this.isMenuUp = false;
},
_onDestroy: function() {
@ -489,6 +723,10 @@ const AppWellIcon = new Lang.Class({
this._removeMenuTimeout();
},
_createIcon: function(iconSize) {
return this.app.create_icon_texture(iconSize);
},
_removeMenuTimeout: function() {
if (this._menuTimeoutId > 0) {
Mainloop.source_remove(this._menuTimeoutId);
@ -546,6 +784,7 @@ const AppWellIcon = new Lang.Class({
popupMenu: function() {
this._removeMenuTimeout();
this.actor.fake_release();
this._draggable.fakeRelease();
if (!this._menu) {
this._menu = new AppIconMenu(this);
@ -561,9 +800,11 @@ const AppWellIcon = new Lang.Class({
this._menuManager.addMenu(this._menu);
}
this.isMenuUp = true;
this.emit('menu-state-changed', true);
this.actor.set_hover(true);
this._menu.popup();
this._menuManager.ignoreRelease();
return false;
},
@ -578,23 +819,20 @@ const AppWellIcon = new Lang.Class({
_onMenuPoppedDown: function() {
this.actor.sync_hover();
this.isMenuUp = false;
this.emit('menu-state-changed', false);
},
_onActivate: function (event) {
this.emit('launching');
let modifiers = event.get_state();
if (this._onActivateOverride) {
this._onActivateOverride(event);
if (modifiers & Clutter.ModifierType.CONTROL_MASK
&& this.app.state == Shell.AppState.RUNNING) {
this.app.open_new_window(-1);
} else {
if (modifiers & Clutter.ModifierType.CONTROL_MASK
&& this.app.state == Shell.AppState.RUNNING) {
this.app.open_new_window(-1);
} else {
this.app.activate();
}
this.app.activate();
}
Main.overview.hide();
},
@ -615,7 +853,7 @@ const AppWellIcon = new Lang.Class({
return this.icon.icon;
}
});
Signals.addSignalMethods(AppWellIcon.prototype);
Signals.addSignalMethods(AppIcon.prototype);
const AppIconMenu = new Lang.Class({
Name: 'AppIconMenu',

View File

@ -84,9 +84,12 @@ const AppFavorites = new Lang.Class({
let app = Shell.AppSystem.get_default().lookup_app(appId);
Main.overview.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () {
this._removeFavorite(appId);
}));
Main.overview.setMessage(_("%s has been added to your favorites.").format(app.get_name()),
{ forFeedback: true,
undoCallback: Lang.bind(this, function () {
this._removeFavorite(appId);
})
});
},
addFavorite: function(appId) {
@ -116,9 +119,11 @@ const AppFavorites = new Lang.Class({
return;
Main.overview.setMessage(_("%s has been removed from your favorites.").format(app.get_name()),
Lang.bind(this, function () {
this._addFavorite(appId, pos);
}));
{ forFeedback: true,
undoCallback: Lang.bind(this, function () {
this._addFavorite(appId, pos);
})
});
}
});
Signals.addSignalMethods(AppFavorites.prototype);

724
js/ui/background.js Normal file
View File

@ -0,0 +1,724 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GDesktopEnums = imports.gi.GDesktopEnums;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GnomeDesktop = imports.gi.GnomeDesktop;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Signals = imports.signals;
const Main = imports.ui.main;
const Params = imports.misc.params;
const Tweener = imports.ui.tweener;
const BACKGROUND_SCHEMA = 'org.gnome.desktop.background';
const PRIMARY_COLOR_KEY = 'primary-color';
const SECONDARY_COLOR_KEY = 'secondary-color';
const COLOR_SHADING_TYPE_KEY = 'color-shading-type';
const BACKGROUND_STYLE_KEY = 'picture-options';
const PICTURE_OPACITY_KEY = 'picture-opacity';
const PICTURE_URI_KEY = 'picture-uri';
const FADE_ANIMATION_TIME = 1.0;
// These parameters affect how often we redraw.
// The first is how different (percent crossfaded) the slide show
// has to look before redrawing and the second is the minimum
// frequency (in seconds) we're willing to wake up
const ANIMATION_OPACITY_STEP_INCREMENT = 4.0;
const ANIMATION_MIN_WAKEUP_INTERVAL = 1.0;
let _backgroundCache = null;
const BackgroundCache = new Lang.Class({
Name: 'BackgroundCache',
_init: function() {
this._patterns = [];
this._images = [];
this._fileMonitors = {};
},
getPatternContent: function(params) {
params = Params.parse(params, { monitorIndex: 0,
color: null,
secondColor: null,
shadingType: null,
effects: Meta.BackgroundEffects.NONE });
let content = null;
let candidateContent = null;
for (let i = 0; i < this._patterns.length; i++) {
if (!this._patterns[i])
continue;
if (this._patterns[i].get_shading() != params.shadingType)
continue;
if (!params.color.equal(this._patterns[i].get_color()))
continue;
if (params.shadingType != GDesktopEnums.BackgroundShading.SOLID &&
!params.secondColor.equal(this._patterns[i].get_second_color()))
continue;
candidateContent = this._patterns[i];
if (params.effects != this._patterns[i].effects)
continue;
break;
}
if (candidateContent) {
content = candidateContent.copy(params.monitorIndex, params.effects);
} else {
content = new Meta.Background({ meta_screen: global.screen,
monitor: params.monitorIndex,
effects: params.effects });
if (params.shadingType == GDesktopEnums.BackgroundShading.SOLID) {
content.load_color(params.color);
} else {
content.load_gradient(params.shadingType, params.color, params.secondColor);
}
this._patterns.push(content);
}
return content;
},
_monitorFile: function(filename) {
if (this._fileMonitors[filename])
return;
let file = Gio.File.new_for_path(filename);
let monitor = file.monitor(Gio.FileMonitorFlags.NONE, null);
let signalId = monitor.connect('changed',
Lang.bind(this, function() {
for (let i = 0; i < this._images.length; i++) {
if (this._images[i].get_filename() == filename)
this._images.splice(i, 1);
}
monitor.disconnect(signalId);
this.emit('file-changed', filename);
}));
this._fileMonitors[filename] = monitor;
},
_removeContent: function(contentList, content) {
let index = contentList.indexOf(content);
if (index >= 0)
contentList.splice(index, 1);
},
removePatternContent: function(content) {
this._removeContent(this._patterns, content);
},
removeImageContent: function(content) {
this._removeContent(this._images, content);
},
getImageContent: function(params) {
params = Params.parse(params, { monitorIndex: 0,
style: null,
filename: null,
effects: Meta.BackgroundEffects.NONE,
cancellable: null,
onFinished: null });
let content = null;
let candidateContent = null;
for (let i = 0; i < this._images.length; i++) {
if (!this._images[i])
continue;
if (this._images[i].get_style() != params.style)
continue;
if (this._images[i].get_filename() != params.filename)
continue;
if (params.style == GDesktopEnums.BackgroundStyle.SPANNED &&
this._images[i].monitor_index != this._monitorIndex)
continue;
candidateContent = this._images[i];
if (params.effects != this._images[i].effects)
continue;
break;
}
if (candidateContent) {
content = candidateContent.copy(params.monitorIndex, params.effects);
if (params.cancellable && params.cancellable.is_cancelled())
content = null;
if (params.onFinished)
params.onFinished(content);
} else {
content = new Meta.Background({ meta_screen: global.screen,
monitor: params.monitorIndex,
effects: params.effects });
content.load_file_async(params.filename,
params.style,
params.cancellable,
Lang.bind(this,
function(object, result) {
try {
content.load_file_finish(result);
this._monitorFile(params.filename);
this._images.push(content);
} catch(e) {
content = null;
}
if (params.onFinished)
params.onFinished(content);
}));
}
},
getAnimation: function(params) {
params = Params.parse(params, { filename: null,
onLoaded: null });
if (this._animationFilename == params.filename) {
if (params.onLoaded) {
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
params.onLoaded(this._animation);
}));
}
}
let animation = new Animation({ filename: params.filename });
animation.load(Lang.bind(this, function() {
this._monitorFile(params.filename);
this._animationFilename = params.filename;
this._animation = animation;
if (params.onLoaded) {
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
params.onLoaded(this._animation);
}));
}
}));
}
});
Signals.addSignalMethods(BackgroundCache.prototype);
function getBackgroundCache() {
if (!_backgroundCache)
_backgroundCache = new BackgroundCache();
return _backgroundCache;
}
const Background = new Lang.Class({
Name: 'Background',
_init: function(params) {
params = Params.parse(params, { monitorIndex: 0,
layoutManager: Main.layoutManager,
effects: Meta.BackgroundEffects.NONE });
this.actor = new Meta.BackgroundGroup();
this.actor._delegate = this;
this._destroySignalId = this.actor.connect('destroy',
Lang.bind(this, this._destroy));
this._settings = new Gio.Settings({ schema: BACKGROUND_SCHEMA });
this._monitorIndex = params.monitorIndex;
this._layoutManager = params.layoutManager;
this._effects = params.effects;
this._fileWatches = {};
this._pattern = null;
// contains a single image for static backgrounds and
// two images (from and to) for slide shows
this._images = {};
this._brightness = 1.0;
this._vignetteSharpness = 0.2;
this._saturation = 1.0;
this._cancellable = new Gio.Cancellable();
this.isLoaded = false;
this._settings.connect('changed', Lang.bind(this, function() {
this.emit('changed');
}));
this._load();
},
_destroy: function() {
this._cancellable.cancel();
if (this._animationUpdateTimeoutId) {
GLib.source_remove (this._animationUpdateTimeoutId);
this._animationUpdateTimeoutId = 0
}
let i;
let keys = Object.keys(this._fileWatches);
for (i = 0; i < keys.length; i++) {
this._cache.disconnect(this._fileWatches[keys[i]]);
}
this._fileWatches = null;
if (this._pattern) {
if (this._pattern.content)
this._cache.removePatternContent(this._pattern.content);
this._pattern.destroy();
this._pattern = null;
}
keys = Object.keys(this._images);
for (i = 0; i < keys.length; i++) {
let actor = this._images[keys[i]];
if (actor.content)
this._cache.removeImageContent(actor.content);
actor.destroy();
this._images[keys[i]] = null;
}
this.actor.disconnect(this._destroySignalId);
this._destroySignalId = 0;
},
_setLoaded: function() {
if (this.isLoaded)
return;
this.isLoaded = true;
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
this.emit('loaded');
return false;
}));
},
_loadPattern: function() {
let colorString, res, color, secondColor;
colorString = this._settings.get_string(PRIMARY_COLOR_KEY);
[res, color] = Clutter.Color.from_string(colorString);
colorString = this._settings.get_string(SECONDARY_COLOR_KEY);
[res, secondColor] = Clutter.Color.from_string(colorString);
let shadingType = this._settings.get_enum(COLOR_SHADING_TYPE_KEY);
let content = this._cache.getPatternContent({ monitorIndex: this._monitorIndex,
effects: this._effects,
color: color,
secondColor: secondColor,
shadingType: shadingType });
this._pattern = new Meta.BackgroundActor();
this.actor.add_child(this._pattern);
this._pattern.content = content;
},
_watchCacheFile: function(filename) {
if (this._fileWatches[filename])
return;
let signalId = this._cache.connect('file-changed',
Lang.bind(this, function(cache, changedFile) {
if (changedFile == filename) {
this.emit('changed');
}
}));
this._fileWatches[filename] = signalId;
},
_addImage: function(content, index, filename) {
content.saturation = this._saturation;
content.brightness = this._brightness;
content.vignette_sharpness = this._vignetteSharpness;
let actor = new Meta.BackgroundActor();
actor.content = content;
this.actor.add_child(actor);
this._images[index] = actor;
this._watchCacheFile(filename);
},
_updateImage: function(content, index, filename) {
content.saturation = this._saturation;
content.brightness = this._brightness;
content.vignette_sharpness = this._vignetteSharpness;
this._images[index].content = content;
this._watchCacheFile(filename);
},
_updateAnimationProgress: function() {
if (this._images[1]) {
this._images[1].raise_top();
this._images[1].opacity = this._animation.transitionProgress * 255;
}
this._queueAnimationUpdate();
},
_updateAnimation: function() {
this._animationUpdateTimeoutId = 0;
let files = this._animation.getKeyFrameFiles(this._layoutManager.monitors[this._monitorIndex]);
if (!files) {
this._setLoaded();
this._queueAnimationUpdate();
return;
}
let numPendingImages = files.length;
for (let i = 0; i < files.length; i++) {
if (this._images[i] && this._images[i].content &&
this._images[i].content.get_filename() == files[i]) {
numPendingImages--;
if (numPendingImages == 0)
this._updateAnimationProgress();
continue;
}
this._cache.getImageContent({ monitorIndex: this._monitorIndex,
effects: this._effects,
style: this._style,
filename: files[i],
cancellable: this._cancellable,
onFinished: Lang.bind(this, function(content) {
numPendingImages--;
if (!content) {
this._setLoaded();
if (numPendingImages == 0)
this._updateAnimationProgress();
return;
}
if (!this._images[i]) {
this._addImage(content, i, files[i]);
} else {
this._updateImage(content, i, files[i]);
}
if (numPendingImages == 0) {
this._setLoaded();
this._updateAnimationProgress();
}
})
});
}
},
_queueAnimationUpdate: function() {
if (this._animationUpdateTimeoutId != 0)
return;
if (!this._cancellable || this._cancellable.is_cancelled())
return;
if (!this._animation.duration)
return;
let interval = Math.max(ANIMATION_MIN_WAKEUP_INTERVAL * 1000,
ANIMATION_OPACITY_STEP_INCREMENT / this._animation.duration);
this._animationUpdateTimeoutId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
interval,
Lang.bind(this, function() {
this._animationUpdateTimeoutId = 0;
this._updateAnimation();
return false;
}));
},
_loadAnimation: function(filename) {
this._cache.getAnimation({ filename: filename,
onLoaded: Lang.bind(this, function(animation) {
this._animation = animation;
if (!this._animation || this._cancellable.is_cancelled()) {
this._setLoaded();
return;
}
this._updateAnimation();
this._watchCacheFile(filename);
})
});
},
_loadFile: function(filename) {
this._cache.getImageContent({ monitorIndex: this._monitorIndex,
effects: this._effects,
style: this._style,
filename: filename,
cancellable: this._cancellable,
onFinished: Lang.bind(this, function(content) {
if (!content) {
if (!this._cancellable.is_cancelled())
this._loadAnimation(filename);
return;
}
this._addImage(content, 0, filename);
this._setLoaded();
})
});
},
_load: function () {
this._cache = getBackgroundCache();
this._loadPattern(this._cache);
this._style = this._settings.get_enum(BACKGROUND_STYLE_KEY);
if (this._style == GDesktopEnums.BackgroundStyle.NONE) {
this._setLoaded();
return;
}
let uri = this._settings.get_string(PICTURE_URI_KEY);
let filename = Gio.File.new_for_uri(uri).get_path();
this._loadFile(filename);
},
get saturation() {
return this._saturation;
},
set saturation(saturation) {
this._saturation = saturation;
if (this._pattern && this._pattern.content)
this._pattern.content.saturation = saturation;
let keys = Object.keys(this._images);
for (let i = 0; i < keys.length; i++) {
let image = this._images[keys[i]];
if (image && image.content)
image.content.saturation = saturation;
}
},
get brightness() {
return this._brightness;
},
set brightness(factor) {
this._brightness = factor;
if (this._pattern && this._pattern.content)
this._pattern.content.brightness = factor;
let keys = Object.keys(this._images);
for (let i = 0; i < keys.length; i++) {
let image = this._images[keys[i]];
if (image && image.content)
image.content.brightness = factor;
}
},
get vignetteSharpness() {
return this._vignetteSharpness;
},
set vignetteSharpness(sharpness) {
this._vignetteSharpness = sharpness;
if (this._pattern && this._pattern.content)
this._pattern.content.vignette_sharpness = sharpness;
let keys = Object.keys(this._images);
for (let i = 0; i < keys.length; i++) {
let image = this._images[keys[i]];
if (image && image.content)
image.content.vignette_sharpness = sharpness;
}
}
});
Signals.addSignalMethods(Background.prototype);
const SystemBackground = new Lang.Class({
Name: 'SystemBackground',
_init: function() {
this._cache = getBackgroundCache();
this.actor = new Meta.BackgroundActor();
this._cache.getImageContent({ style: GDesktopEnums.BackgroundStyle.WALLPAPER,
filename: global.datadir + '/theme/noise-texture.png',
effects: Meta.BackgroundEffects.NONE,
onFinished: Lang.bind(this, function(content) {
this.actor.content = content;
this.emit('loaded');
})
});
}
});
Signals.addSignalMethods(SystemBackground.prototype);
const Animation = new Lang.Class({
Name: 'Animation',
_init: function(params) {
params = Params.parse(params, { filename: null });
this.filename = params.filename;
this._keyFrames = [];
this.duration = 0.0;
this.transitionProgress = 0.0;
this.loaded = false;
},
load: function(callback) {
let file = Gio.File.new_for_path(this.filename);
this._show = new GnomeDesktop.BGSlideShow({ filename: this.filename });
this._show.load_async(null,
Lang.bind(this,
function(object, result) {
this.duration = this._show.get_total_duration();
this.loaded = true;
if (callback)
callback();
}));
},
getKeyFrameFiles: function(monitor) {
if (!this._show)
return null;
if (this._show.get_num_slides() < 1)
return null;
let [progress, duration, isFixed, file1, file2] = this._show.get_current_slide(monitor.width, monitor.height);
this.transitionProgress = progress;
let files = [];
if (file1)
files.push(file1);
if (file2)
files.push(file2);
return files;
},
});
Signals.addSignalMethods(Animation.prototype);
const BackgroundManager = new Lang.Class({
Name: 'BackgroundManager',
_init: function(params) {
params = Params.parse(params, { container: null,
layoutManager: Main.layoutManager,
monitorIndex: null,
effects: Meta.BackgroundEffects.NONE,
controlPosition: true });
this._container = params.container;
this._layoutManager = params.layoutManager;
this._effects = params.effects;
this._monitorIndex = params.monitorIndex;
this._controlPosition = params.controlPosition;
this.background = this._createBackground();
this._newBackground = null;
this._loadedSignalId = 0;
this._changedSignalId = 0;
},
destroy: function() {
if (this._loadedSignalId)
this._newBackground.disconnect(this._loadedSignalId);
if (this._changedSignalId)
this.background.disconnect(this._changedSignalId);
if (this._newBackground) {
this._newBackground.actor.destroy();
this._newBackground = null;
}
if (this.background) {
this.background.actor.destroy();
this.background = null;
}
},
_updateBackground: function(background, monitorIndex) {
let newBackground = this._createBackground(monitorIndex);
newBackground.vignetteSharpness = background.vignetteSharpness;
newBackground.brightness = background.brightness;
newBackground.saturation = background.saturation;
newBackground.visible = background.visible;
let signalId = newBackground.connect('loaded',
Lang.bind(this, function() {
newBackground.disconnect(signalId);
Tweener.addTween(background.actor,
{ opacity: 0,
time: FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.background = newBackground;
this._newBackground = null;
background.actor.destroy();
this.emit('changed');
})
});
}));
this._loadedSignalId = signalId;
this._newBackground = newBackground;
},
_createBackground: function() {
let background = new Background({ monitorIndex: this._monitorIndex,
layoutManager: this._layoutManager,
effects: this._effects });
this._container.add_child(background.actor);
let monitor = this._layoutManager.monitors[this._monitorIndex];
background.actor.set_size(monitor.width, monitor.height);
if (this._controlPosition) {
background.actor.set_position(monitor.x, monitor.y);
background.actor.lower_bottom();
}
let signalId = background.connect('changed', Lang.bind(this, function() {
background.disconnect(signalId);
this._updateBackground(background, this._monitorIndex);
}));
this._changedSignalId = signalId;
return background;
},
});
Signals.addSignalMethods(BackgroundManager.prototype);

58
js/ui/backgroundMenu.js Normal file
View File

@ -0,0 +1,58 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const BoxPointer = imports.ui.boxpointer;
const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const BackgroundMenu = new Lang.Class({
Name: 'BackgroundMenu',
Extends: PopupMenu.PopupMenu,
_init: function(source) {
this.parent(source, 0, St.Side.TOP);
this.addSettingsAction(_("Settings"), 'gnome-control-center.desktop');
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.addSettingsAction(_("Change Background…"), 'gnome-background-panel.desktop');
this.actor.add_style_class_name('background-menu');
Main.uiGroup.add_actor(this.actor);
this.actor.hide();
}
});
function addBackgroundMenu(actor) {
let cursor = new St.Bin({ opacity: 0 });
Main.uiGroup.add_actor(cursor);
actor.reactive = true;
actor._backgroundMenu = new BackgroundMenu(cursor);
actor._backgroundManager = new PopupMenu.PopupMenuManager({ actor: actor });
actor._backgroundManager.addMenu(actor._backgroundMenu);
function openMenu() {
let [x, y] = global.get_pointer();
cursor.set_position(x, y);
actor._backgroundMenu.open(BoxPointer.PopupAnimation.NONE);
}
let clickAction = new Clutter.ClickAction();
clickAction.connect('long-press', function(action, actor, state) {
if (state == Clutter.LongPressState.QUERY)
return action.get_button() == 1 && !actor._backgroundMenu.isOpen;
if (state == Clutter.LongPressState.ACTIVATE)
openMenu();
return true;
});
clickAction.connect('clicked', function(action) {
if (action.get_button() == 3)
openMenu();
});
actor.add_action(clickAction);
}

View File

@ -38,6 +38,7 @@ const BoxPointer = new Lang.Class({
this._arrowSide = arrowSide;
this._userArrowSide = arrowSide;
this._arrowOrigin = 0;
this._arrowActor = null;
this.actor = new St.Bin({ x_fill: true,
y_fill: true });
this._container = new Shell.GenericContainer();
@ -220,31 +221,27 @@ const BoxPointer = new Lang.Class({
this.bin.allocate(childBox, flags);
if (this._sourceActor && this._sourceActor.mapped) {
this._reposition(this._sourceActor, this._arrowAlignment);
if (this._shouldFlip()) {
switch (this._arrowSide) {
case St.Side.TOP:
this._arrowSide = St.Side.BOTTOM;
break;
case St.Side.BOTTOM:
this._arrowSide = St.Side.TOP;
break;
case St.Side.LEFT:
this._arrowSide = St.Side.RIGHT;
break;
case St.Side.RIGHT:
this._arrowSide = St.Side.LEFT;
break;
}
this._reposition(this._sourceActor, this._arrowAlignment);
}
this._reposition();
this._updateFlip();
}
},
_drawBorder: function(area) {
let themeNode = this.actor.get_theme_node();
if (this._arrowActor) {
let [sourceX, sourceY] = this._arrowActor.get_transformed_position();
let [sourceWidth, sourceHeight] = this._arrowActor.get_transformed_size();
let [absX, absY] = this.actor.get_transformed_position();
if (this._arrowSide == St.Side.TOP ||
this._arrowSide == St.Side.BOTTOM) {
this._arrowOrigin = sourceX - absX + sourceWidth / 2;
} else {
this._arrowOrigin = sourceY - absY + sourceHeight / 2;
}
}
let borderWidth = themeNode.get_length('-arrow-border-width');
let base = themeNode.get_length('-arrow-base');
let rise = themeNode.get_length('-arrow-rise');
@ -405,11 +402,11 @@ const BoxPointer = new Lang.Class({
cr.setLineWidth(borderWidth);
cr.stroke();
}
cr.$dispose();
},
setPosition: function(sourceActor, alignment) {
this._arrowSide = this._userArrowSide;
// We need to show it now to force an allocation,
// so that we can query the correct size.
this.actor.show();
@ -417,7 +414,8 @@ const BoxPointer = new Lang.Class({
this._sourceActor = sourceActor;
this._arrowAlignment = alignment;
this._reposition(sourceActor, alignment);
this._reposition();
this._updateFlip();
},
setSourceAlignment: function(alignment) {
@ -429,7 +427,10 @@ const BoxPointer = new Lang.Class({
this.setPosition(this._sourceActor, this._arrowAlignment);
},
_reposition: function(sourceActor, alignment) {
_reposition: function() {
let sourceActor = this._sourceActor;
let alignment = this._arrowAlignment;
// Position correctly relative to the sourceActor
let sourceNode = sourceActor.get_theme_node();
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
@ -550,10 +551,20 @@ const BoxPointer = new Lang.Class({
}
},
// @actor: an actor relative to which the arrow is positioned.
// Differently from setPosition, this will not move the boxpointer itself,
// on the arrow
setArrowActor: function(actor) {
if (this._arrowActor != actor) {
this._arrowActor = actor;
this._border.queue_repaint();
}
},
_shiftActor : function() {
// Since the position of the BoxPointer depends on the allocated size
// of the BoxPointer and the position of the source actor, trying
// to position the BoxPoiner via the x/y properties will result in
// to position the BoxPointer via the x/y properties will result in
// allocation loops and warnings. Instead we do the positioning via
// the anchor point, which is independent of allocation, and leave
// x == y == 0.
@ -561,37 +572,47 @@ const BoxPointer = new Lang.Class({
-(this._yPosition + this._yOffset));
},
_shouldFlip: function() {
_calculateArrowSide: function(arrowSide) {
let sourceAllocation = Shell.util_get_transformed_allocation(this._sourceActor);
let boxAllocation = Shell.util_get_transformed_allocation(this.actor);
let boxWidth = boxAllocation.x2 - boxAllocation.x1;
let boxHeight = boxAllocation.y2 - boxAllocation.y1;
let [minWidth, minHeight, boxWidth, boxHeight] = this._container.get_preferred_size();
let monitor = Main.layoutManager.findMonitorForActor(this.actor);
switch (this._arrowSide) {
switch (arrowSide) {
case St.Side.TOP:
if (boxAllocation.y2 > monitor.y + monitor.height &&
if (sourceAllocation.y2 + boxHeight > monitor.y + monitor.height &&
boxHeight < sourceAllocation.y1 - monitor.y)
return true;
return St.Side.BOTTOM;
break;
case St.Side.BOTTOM:
if (boxAllocation.y1 < monitor.y &&
if (sourceAllocation.y1 - boxHeight < monitor.y &&
boxHeight < monitor.y + monitor.height - sourceAllocation.y2)
return true;
return St.Side.TOP;
break;
case St.Side.LEFT:
if (boxAllocation.x2 > monitor.x + monitor.width &&
if (sourceAllocation.y2 + boxWidth > monitor.x + monitor.width &&
boxWidth < sourceAllocation.x1 - monitor.x)
return true;
return St.Side.RIGHT;
break;
case St.Side.RIGHT:
if (boxAllocation.x1 < monitor.x &&
if (sourceAllocation.y1 - boxWidth < monitor.x &&
boxWidth < monitor.x + monitor.width - sourceAllocation.x2)
return true;
return St.Side.LEFT;
break;
}
return false;
return arrowSide;
},
_updateFlip: function() {
let arrowSide = this._calculateArrowSide(this._userArrowSide);
if (this._arrowSide != arrowSide) {
this._arrowSide = arrowSide;
this._reposition();
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
this._container.queue_relayout();
return false;
}));
}
},
set xOffset(offset) {

View File

@ -2,6 +2,7 @@
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const St = imports.gi.St;
const Signals = imports.signals;
@ -11,7 +12,6 @@ const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
const WEEKDATE_HEADER_WIDTH_DIGITS = 3;
const SHOW_WEEKDATE_KEY = 'show-weekdate';
// in org.gnome.desktop.interface
@ -63,15 +63,18 @@ function _formatEventTime(event, clockFormat) {
} else {
switch (clockFormat) {
case '24h':
/* Translators: Shown in calendar event list, if 24h format */
ret = event.date.toLocaleFormat(C_("event list time", "%H:%M"));
/* Translators: Shown in calendar event list, if 24h format,
\u2236 is a ratio character, similar to : */
ret = event.date.toLocaleFormat(C_("event list time", "%H\u2236%M"));
break;
default:
/* explicit fall-through */
case '12h':
/* Transators: Shown in calendar event list, if 12h format */
ret = event.date.toLocaleFormat(C_("event list time", "%l:%M %p"));
/* Transators: Shown in calendar event list, if 12h format,
\u2236 is a ratio character, similar to : and \u2009 is
a thin space */
ret = event.date.toLocaleFormat(C_("event list time", "%l\u2236%M\u2009%p"));
break;
}
}
@ -95,15 +98,6 @@ function _getCalendarWeekForDate(date) {
return weekNumber;
}
function _getDigitWidth(actor){
let context = actor.get_pango_context();
let themeNode = actor.get_theme_node();
let font = themeNode.get_font();
let metrics = context.get_metrics(font, context.get_language());
let width = metrics.get_approximate_digit_width();
return width;
}
function _getCalendarDayAbbreviation(dayNumber) {
let abbreviations = [
/* Translators: Calendar grid abbreviation for Sunday.
@ -203,15 +197,12 @@ const CalendarServerIface = <interface name="org.gnome.Shell.CalendarServer">
const CalendarServerInfo = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIface);
function CalendarServer() {
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
g_interface_name: CalendarServerInfo.name,
g_interface_info: CalendarServerInfo,
g_name: 'org.gnome.Shell.CalendarServer',
g_object_path: '/org/gnome/Shell/CalendarServer',
g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES });
self.init(null);
return self;
return new Gio.DBusProxy({ g_connection: Gio.DBus.session,
g_interface_name: CalendarServerInfo.name,
g_interface_info: CalendarServerInfo,
g_name: 'org.gnome.Shell.CalendarServer',
g_object_path: '/org/gnome/Shell/CalendarServer',
g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES });
}
function _datesEqual(a, b) {
@ -239,14 +230,27 @@ const DBusEventSource = new Lang.Class({
_init: function() {
this._resetCache();
this._initialized = false;
this._dbusProxy = new CalendarServer();
this._dbusProxy.connectSignal('Changed', Lang.bind(this, this._onChanged));
this._dbusProxy.init_async(GLib.PRIORITY_DEFAULT, null, Lang.bind(this, function(object, result) {
try {
this._dbusProxy.init_finish(result);
} catch(e) {
log('Error loading calendars: ' + e.message);
return;
}
this._dbusProxy.connect('notify::g-name-owner', Lang.bind(this, function() {
if (this._dbusProxy.g_name_owner)
this._onNameAppeared();
else
this._onNameVanished();
this._dbusProxy.connectSignal('Changed', Lang.bind(this, this._onChanged));
this._dbusProxy.connect('notify::g-name-owner', Lang.bind(this, function() {
if (this._dbusProxy.g_name_owner)
this._onNameAppeared();
else
this._onNameVanished();
}));
this._initialized = true;
this._onNameAppeared();
}));
},
@ -293,6 +297,10 @@ const DBusEventSource = new Lang.Class({
},
_loadEvents: function(forceReload) {
// Ignore while loading
if (!this._initialized)
return;
if (this._curRequestBegin && this._curRequestEnd){
let callFlags = Gio.DBusCallFlags.NO_AUTO_START;
if (forceReload)
@ -345,8 +353,6 @@ const Calendar = new Lang.Class({
_init: function() {
this._weekStart = Shell.util_get_week_start();
this._weekdate = NaN;
this._digitWidth = NaN;
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.calendar' });
this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange));
@ -419,8 +425,6 @@ const Calendar = new Lang.Class({
this.actor.add(this._topBox,
{ row: 0, col: 0, col_span: offsetCols + 7 });
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChange));
let back = new St.Button({ style_class: 'calendar-change-month-back' });
this._topBox.add(back);
back.connect('clicked', Lang.bind(this, this._onPrevMonthButtonClicked));
@ -457,18 +461,6 @@ const Calendar = new Lang.Class({
this._firstDayIndex = this.actor.get_n_children();
},
_onStyleChange: function(actor, event) {
// width of a digit in pango units
this._digitWidth = _getDigitWidth(this.actor) / Pango.SCALE;
this._setWeekdateHeaderWidth();
},
_setWeekdateHeaderWidth: function() {
if (this.digitWidth != NaN && this._useWeekdate && this._weekdateHeader) {
this._weekdateHeader.set_width (this._digitWidth * WEEKDATE_HEADER_WIDTH_DIGITS);
}
},
_onScroll : function(actor, event) {
switch (event.get_scroll_direction()) {
case Clutter.ScrollDirection.UP:

View File

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

View File

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

View File

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

View File

@ -587,18 +587,19 @@ const NetworkAgent = new Lang.Class({
Name: 'NetworkAgent',
_init: function() {
this._native = new Shell.NetworkAgent({ auto_register: false,
identifier: 'org.gnome.Shell.NetworkAgent' });
this._native = new Shell.NetworkAgent({ identifier: 'org.gnome.Shell.NetworkAgent' });
this._dialogs = { };
this._vpnRequests = { };
this._native.connect('new-request', Lang.bind(this, this._newRequest));
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
this._enabled = false;
},
enable: function() {
this._native.register();
this._enabled = true;
},
disable: function() {
@ -612,10 +613,15 @@ const NetworkAgent = new Lang.Class({
this._vpnRequests[requestId].cancel(true);
this._vpnRequests = { };
this._native.unregister();
this._enabled = false;
},
_newRequest: function(agent, requestId, connection, settingName, hints, flags) {
if (!this._enabled) {
agent.respond(requestId, Shell.NetworkAgentResponse.USER_CANCELED);
return;
}
if (settingName == 'vpn') {
this._vpnRequest(requestId, connection, hints, flags);
return;

View File

@ -49,7 +49,7 @@ const AuthenticationDialog = new Lang.Class({
let messageBox = new St.BoxLayout({ style_class: 'prompt-dialog-message-layout',
vertical: true });
mainContentBox.add(messageBox,
{ y_align: St.Align.START });
{ expand: true, y_align: St.Align.START });
this._subjectLabel = new St.Label({ style_class: 'prompt-dialog-headline',
text: _("Authentication Required") });
@ -159,14 +159,13 @@ const AuthenticationDialog = new Lang.Class({
messageBox.add(this._nullMessageLabel);
this._nullMessageLabel.show();
this.setButtons([{ label: _("Cancel"),
action: Lang.bind(this, this.cancel),
key: Clutter.Escape
},
{ label: _("Authenticate"),
action: Lang.bind(this, this._onAuthenticateButtonPressed),
default: true
}]);
this._cancelButton = this.addButton({ label: _("Cancel"),
action: Lang.bind(this, this.cancel),
key: Clutter.Escape });
this._okButton = this.addButton({ label: _("Authenticate"),
action: Lang.bind(this, this._onAuthenticateButtonPressed),
default: true },
{ expand: true, x_fill: false, x_align: St.Align.END });
this._doneEmitted = false;
@ -214,8 +213,17 @@ const AuthenticationDialog = new Lang.Class({
}
},
_updateSensitivity: function(sensitive) {
this._passwordEntry.reactive = sensitive;
this._passwordEntry.clutter_text.editable = sensitive;
this._okButton.can_focus = sensitive;
this._okButton.reactive = sensitive;
},
_onEntryActivate: function() {
let response = this._passwordEntry.get_text();
this._updateSensitivity(false);
this._session.response(response);
// When the user responds, dismiss already shown info and
// error texts (if any)
@ -269,6 +277,7 @@ const AuthenticationDialog = new Lang.Class({
this._passwordBox.show();
this._passwordEntry.set_text('');
this._passwordEntry.grab_key_focus();
this._updateSensitivity(true);
this._ensureOpen();
},
@ -326,11 +335,19 @@ const AuthenticationAgent = new Lang.Class({
},
enable: function() {
this._native.register();
try {
this._native.register();
} catch(e) {
log('Failed to register AuthenticationAgent');
}
},
disable: function() {
this._native.unregister();
try {
this._native.unregister();
} catch(e) {
log('Failed to unregister AuthenticationAgent');
}
},
_onInitiate: function(nativeAgent, actionId, message, iconName, cookie, userNames) {

View File

@ -1,5 +1,6 @@
const Lang = imports.lang;
const Main = imports.ui.main;
const Gio = imports.gi.Gio;
const Meta = imports.gi.Meta;
@ -16,13 +17,16 @@ const Recorder = new Lang.Class({
},
enable: function() {
global.display.add_keybinding('toggle-recording',
this._bindingSettings,
Meta.KeyBindingFlags.NONE, Lang.bind(this, this._toggleRecorder));
Main.wm.addKeybinding('toggle-recording',
this._bindingSettings,
Meta.KeyBindingFlags.NONE,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._toggleRecorder));
},
disable: function() {
global.display.remove_keybinding('toggle-recording');
Main.wm.removeKeybinding('toggle-recording');
},
_ensureRecorder: function() {

View File

@ -13,6 +13,7 @@ const Tp = imports.gi.TelepathyGLib;
const History = imports.misc.history;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon;
const Params = imports.misc.params;
const PopupMenu = imports.ui.popupMenu;
@ -383,25 +384,12 @@ const TelepathyClient = new Lang.Class({
}
/* Display notification to ask user to accept/reject request */
let source = this._ensureSubscriptionSource();
let source = this._ensureAppSource();
let notif = new SubscriptionRequestNotification(source, contact);
source.notify(notif);
},
_ensureSubscriptionSource: function() {
if (this._subscriptionSource == null) {
this._subscriptionSource = new MessageTray.Source(_("Subscription request"),
'gtk-dialog-question');
Main.messageTray.add(this._subscriptionSource);
this._subscriptionSource.connect('destroy', Lang.bind(this, function () {
this._subscriptionSource = null;
}));
}
return this._subscriptionSource;
},
_accountConnectionStatusNotifyCb: function(account) {
let connectionError = account.connection_error;
@ -415,7 +403,7 @@ const TelepathyClient = new Lang.Class({
return;
/* Display notification that account failed to connect */
let source = this._ensureAccountSource();
let source = this._ensureAppSource();
notif = new AccountNotification(source, account, connectionError);
this._accountNotifications[account.get_object_path()] = notif;
@ -425,17 +413,18 @@ const TelepathyClient = new Lang.Class({
source.notify(notif);
},
_ensureAccountSource: function() {
if (this._accountSource == null) {
this._accountSource = new MessageTray.Source(_("Connection error"),
'gtk-dialog-error');
Main.messageTray.add(this._accountSource);
this._accountSource.connect('destroy', Lang.bind(this, function () {
this._accountSource = null;
_ensureAppSource: function() {
if (this._appSource == null) {
this._appSource = new MessageTray.Source(_("Chat"), 'empathy');
this._appSource.policy = new NotificationDaemon.NotificationApplicationPolicy('empathy');
Main.messageTray.add(this._appSource);
this._appSource.connect('destroy', Lang.bind(this, function () {
this._appSource = null;
}));
}
return this._accountSource;
return this._appSource;
}
});
@ -492,12 +481,16 @@ const ChatSource = new Lang.Class({
}));
item.connect('activate', Lang.bind(this, function() {
this.setMuted(!this.isMuted);
this.emit('done-displaying-content');
this.emit('done-displaying-content', false);
}));
rightClickMenu.add(item.actor);
return rightClickMenu;
},
_createPolicy: function() {
return new NotificationDaemon.NotificationApplicationPolicy('empathy');
},
_updateAlias: function() {
let oldAlias = this.title;
let newAlias = this._contact.get_alias();
@ -647,6 +640,10 @@ const ChatSource = new Lang.Class({
return this._pendingMessages.length;
},
get indicatorCount() {
return this.count;
},
get unseenCount() {
return this.count;
},
@ -744,8 +741,7 @@ const ChatSource = new Lang.Class({
_ackMessages: function() {
// Don't clear our messages here, tp-glib will send a
// 'pending-message-removed' for each one.
this._channel.ack_all_pending_messages_async(Lang.bind(this, function(src, result) {
this._channel.ack_all_pending_messages_finish(result);}));
this._channel.ack_all_pending_messages_async(null);
}
});
@ -771,16 +767,19 @@ const ChatNotification = new Lang.Class({
this.emit('unfocused');
}));
this._oldMaxScrollAdjustment = 0;
this._createScrollArea();
this._lastGroup = null;
this._lastGroupActor = null;
// Keep track of the bottom position for the current adjustment and
// force a scroll to the bottom if things change while we were at the
// bottom
this._oldMaxScrollValue = this._scrollArea.vscroll.adjustment.value;
this._scrollArea.add_style_class_name('chat-notification-scrollview');
this._scrollArea.vscroll.adjustment.connect('changed', Lang.bind(this, function(adjustment) {
let currentValue = adjustment.value + adjustment.page_size;
if (currentValue == this._oldMaxScrollAdjustment)
if (adjustment.value == this._oldMaxScrollValue)
this.scrollTo(St.Side.BOTTOM);
this._oldMaxScrollAdjustment = adjustment.upper;
this._oldMaxScrollValue = Math.max(adjustment.lower, adjustment.upper - adjustment.page_size);
}));
this._inputHistory = new History.HistoryManager({ entry: this._responseEntry.clutter_text });
@ -938,26 +937,24 @@ const ChatNotification = new Lang.Class({
}
// 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"*/
/* Translators: this is the word "Yesterday" followed by a time string. i.e. "Yesterday, 14:30"*/
// xgettext:no-c-format
format = _("<b>%H:%M</b> on Yesterday");
format = _("<b>Yesterday</b>, <b>%H:%M</b>");
}
// Show a week day and time if date is in the last week
else if (daysAgo < 7) {
/* Translators: this is a time format string followed by a week day name. i.e. "14:30 on Monday*/
/* Translators: this is the week day name followed by a time string. i.e. "Monday, 14:30*/
// xgettext:no-c-format
format = _("<b>%H:%M</b> on <b>%A</b>");
format = _("<b>%A</b>, <b>%H:%M</b>");
} else if (date.getYear() == now.getYear()) {
/* Translators: this is a time format in the style of "14:30 on Wednesday, May 25",
shown when you get a chat message in the same year */
/* Translators: this is the month name and day number followed by a time string. i.e. "May 25, 14:30"*/
// xgettext:no-c-format
format = _("<b>%H:%M</b> on <b>%A</b>, <b>%B</b> <b>%d</b>");
format = _("<b>%B</b> <b>%d</b>, <b>%H:%M</b>");
} else {
/* Translators: this is a time format in the style of "14:30 on Wednesday, May 25, 2012",
shown when you get a chat message in a different year */
/* Translators: this is the month name, day number, year number followed by a time string. i.e. "May 25 2012, 14:30"*/
// xgettext:no-c-format
format = _("<b>%H:%M</b> on <b>%A</b>, <b>%B</b> <b>%d</b>, %Y");
format = _("<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> ");
}
return date.toLocaleFormat(format);
@ -1062,6 +1059,10 @@ const ApproverSource = new Lang.Class({
}));
},
_createPolicy: function() {
return new NotificationDaemon.NotificationApplicationPolicy('empathy');
},
destroy: function() {
if (this._invalidId != 0) {
this._dispatchOp.disconnect(this._invalidId);
@ -1133,7 +1134,7 @@ const AudioVideoNotification = new Lang.Class({
this.parent(source, title, null, { customContent: true });
this.setResident(true);
this.addButton('reject', _("Reject"));
this.addButton('reject', _("Decline"));
/* translators: this is a button label (verb), not a noun */
this.addButton('answer', _("Answer"));
@ -1350,29 +1351,18 @@ const AccountNotification = new Lang.Class({
this.parent(source,
/* translators: argument is the account name, like
* name@jabber.org for example. */
_("Connection to %s failed").format(account.get_display_name()),
null, { customContent: true });
this._label = new St.Label();
this.addActor(this._label);
this._updateMessage(connectionError);
_("Unable to connect to %s").format(account.get_display_name()),
this._getMessage(connectionError));
this._account = account;
this.addButton('reconnect', _("Reconnect"));
this.addButton('edit', _("Edit account"));
this.addButton('view', _("View account"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'reconnect':
// If it fails again, a new notification should pop up with the
// new error.
account.reconnect_async(null);
break;
case 'edit':
let cmd = '/usr/bin/empathy-accounts'
+ ' --select-account=%s'
.format(account.get_path_suffix());
case 'view':
let cmd = 'empathy-accounts --select-account=' +
account.get_path_suffix();
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0);
app_info.launch([], global.create_app_launch_context());
break;
@ -1395,19 +1385,19 @@ const AccountNotification = new Lang.Class({
if (status == Tp.ConnectionStatus.CONNECTED) {
this.destroy();
} else if (status == Tp.ConnectionStatus.DISCONNECTED) {
this._updateMessage(account.connection_error);
this.update(this.title, this._getMessage(account.connection_error));
}
}));
},
_updateMessage: function(connectionError) {
_getMessage: function(connectionError) {
let message;
if (connectionError in _connectionErrorMessages) {
message = _connectionErrorMessages[connectionError];
} else {
message = _("Unknown reason");
}
this._label.set_text(message);
return message;
},
destroy: function() {

View File

@ -1,15 +1,14 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gdk = imports.gi.Gdk;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const AltTab = imports.ui.altTab;
const Main = imports.ui.main;
const SwitcherPopup = imports.ui.switcherPopup;
const Params = imports.misc.params;
const Tweener = imports.ui.tweener;
@ -27,6 +26,9 @@ const CtrlAltTabManager = new Lang.Class({
_init: function() {
this._items = [];
this.addGroup(global.window_group, _("Windows"),
'emblem-documents-symbolic', { sortGroup: SortGroup.TOP,
focusCallback: Lang.bind(this, this._focusWindows) });
},
addGroup: function(root, name, icon, params) {
@ -40,11 +42,13 @@ const CtrlAltTabManager = new Lang.Class({
this._items.push(item);
root.connect('destroy', Lang.bind(this, function() { this.removeGroup(root); }));
global.focus_manager.add_group(root);
if (root instanceof St.Widget)
global.focus_manager.add_group(root);
},
removeGroup: function(root) {
global.focus_manager.remove_group(root);
if (root instanceof St.Widget)
global.focus_manager.remove_group(root);
for (let i = 0; i < this._items.length; i++) {
if (this._items[i].root == root) {
this._items.splice(i, 1);
@ -53,11 +57,9 @@ const CtrlAltTabManager = new Lang.Class({
}
},
focusGroup: function(item) {
if (item.window) {
Main.activateWindow(item.window);
} else if (item.focusCallback) {
item.focusCallback();
focusGroup: function(item, timestamp) {
if (item.focusCallback) {
item.focusCallback(timestamp);
} else {
if (global.stage_input_mode == Shell.StageInputMode.NONREACTIVE ||
global.stage_input_mode == Shell.StageInputMode.NORMAL)
@ -75,24 +77,14 @@ const CtrlAltTabManager = new Lang.Class({
if (a.sortGroup != b.sortGroup)
return a.sortGroup - b.sortGroup;
let y;
if (a.x == undefined) {
if (a.window)
a.x = a.window.get_compositor_private().x;
else
[a.x, y] = a.proxy.get_transformed_position();
}
if (b.x == undefined) {
if (b.window)
b.x = b.window.get_compositor_private().x;
else
[b.x, y] = b.proxy.get_transformed_position();
}
let ax, bx, y;
[ax, y] = a.proxy.get_transformed_position();
[bx, y] = b.proxy.get_transformed_position();
return a.x - b.x;
return ax - bx;
},
popup: function(backwards, mask) {
popup: function(backward, binding, mask) {
// Start with the set of focus groups that are currently mapped
let items = this._items.filter(function (item) { return item.proxy.mapped; });
@ -110,8 +102,7 @@ const CtrlAltTabManager = new Lang.Class({
icon = app.create_icon_texture(POPUP_APPICON_SIZE);
else
icon = textureCache.bind_pixbuf_property(windows[i], 'icon');
items.push({ window: windows[i],
name: windows[i].title,
items.push({ name: windows[i].title,
iconActor: icon,
sortGroup: SortGroup.MIDDLE });
}
@ -123,187 +114,63 @@ const CtrlAltTabManager = new Lang.Class({
items.sort(Lang.bind(this, this._sortItems));
if (!this._popup) {
this._popup = new CtrlAltTabPopup();
this._popup.show(items, backwards, mask);
this._popup = new CtrlAltTabPopup(items);
this._popup.show(backward, binding, mask);
this._popup.actor.connect('destroy',
Lang.bind(this, function() {
this._popup = null;
}));
}
},
_focusWindows: function(timestamp) {
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
global.stage.key_focus = null;
global.screen.focus_default_window(timestamp);
}
});
function mod(a, b) {
return (a + b) % b;
}
const CtrlAltTabPopup = new Lang.Class({
Name: 'CtrlAltTabPopup',
Extends: SwitcherPopup.SwitcherPopup,
_init : function() {
this.actor = new Shell.GenericContainer({ name: 'ctrlAltTabPopup',
reactive: true });
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate));
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._haveModal = false;
this._modifierMask = 0;
this._selection = 0;
Main.uiGroup.add_actor(this.actor);
_createSwitcher: function() {
this._switcherList = new CtrlAltTabSwitcher(this._items);
return true;
},
_getPreferredWidth: function (actor, forHeight, alloc) {
let primary = Main.layoutManager.primaryMonitor;
alloc.min_size = primary.width;
alloc.natural_size = primary.width;
},
_getPreferredHeight: function (actor, forWidth, alloc) {
let primary = Main.layoutManager.primaryMonitor;
alloc.min_size = primary.height;
alloc.natural_size = primary.height;
},
_allocate: function (actor, box, flags) {
let childBox = new Clutter.ActorBox();
let primary = Main.layoutManager.primaryMonitor;
let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT);
let vPadding = this.actor.get_theme_node().get_vertical_padding();
let hPadding = this.actor.get_theme_node().get_horizontal_padding();
let [childMinHeight, childNaturalHeight] = this._switcher.actor.get_preferred_height(primary.width - hPadding);
let [childMinWidth, childNaturalWidth] = this._switcher.actor.get_preferred_width(childNaturalHeight);
childBox.x1 = Math.max(primary.x + leftPadding, primary.x + Math.floor((primary.width - childNaturalWidth) / 2));
childBox.x2 = Math.min(primary.x + primary.width - hPadding, childBox.x1 + childNaturalWidth);
childBox.y1 = primary.y + Math.floor((primary.height - childNaturalHeight) / 2);
childBox.y2 = childBox.y1 + childNaturalHeight;
this._switcher.actor.allocate(childBox, flags);
},
show : function(items, startBackwards, mask) {
if (!Main.pushModal(this.actor))
return false;
this._haveModal = true;
this._modifierMask = AltTab.primaryModifier(mask);
this._keyPressEventId = this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
this._keyReleaseEventId = this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
this._items = items;
this._switcher = new CtrlAltTabSwitcher(items);
this.actor.add_actor(this._switcher.actor);
if (startBackwards)
this._selection = this._items.length - 1;
this._select(this._selection);
let [x, y, mods] = global.get_pointer();
if (!(mods & this._modifierMask)) {
this._finish();
return false;
_initialSelection: function(backward, binding) {
if (binding == 'switch-panels') {
if (backward)
this._selectedIndex = this._items.length - 1;
} else if (binding == 'switch-panels-backward') {
if (!backward)
this._selectedIndex = this._items.length - 1;
}
this.actor.opacity = 0;
this.actor.show();
Tweener.addTween(this.actor,
{ opacity: 255,
time: POPUP_FADE_TIME,
transition: 'easeOutQuad'
});
return true;
this._select(this._selectedIndex);
},
_next : function() {
return mod(this._selection + 1, this._items.length);
},
_previous : function() {
return mod(this._selection - 1, this._items.length);
},
_keyPressEvent : function(actor, event) {
let keysym = event.get_key_symbol();
let shift = (event.get_state() & Clutter.ModifierType.SHIFT_MASK);
if (shift && keysym == Clutter.KEY_Tab)
keysym = Clutter.ISO_Left_Tab;
if (keysym == Clutter.KEY_Escape)
this.destroy();
else if (keysym == Clutter.KEY_Tab)
this._select(this._next());
else if (keysym == Clutter.KEY_ISO_Left_Tab)
_keyPressHandler: function(keysym, backwards, action) {
if (action == Meta.KeyBindingAction.SWITCH_PANELS)
this._select(backwards ? this._previous() : this._next());
else if (action == Meta.KeyBindingAction.SWITCH_PANELS_BACKWARD)
this._select(backwards ? this._next() : this._previous());
else if (keysym == Clutter.Left)
this._select(this._previous());
else if (keysym == Clutter.KEY_Left)
this._select(this._previous());
else if (keysym == Clutter.KEY_Right)
else if (keysym == Clutter.Right)
this._select(this._next());
return true;
},
_keyReleaseEvent : function(actor, event) {
let [x, y, mods] = global.get_pointer();
let state = mods & this._modifierMask;
if (state == 0)
this._finish();
return true;
_finish : function(time) {
this.parent(time);
Main.ctrlAltTabManager.focusGroup(this._items[this._selectedIndex], time);
},
_finish : function() {
this.destroy();
Main.ctrlAltTabManager.focusGroup(this._items[this._selection]);
},
_popModal: function() {
if (this._haveModal) {
Main.popModal(this.actor);
this._haveModal = false;
}
},
destroy : function() {
this._popModal();
Tweener.addTween(this.actor,
{ opacity: 0,
time: POPUP_FADE_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
this.actor.destroy();
})
});
},
_onDestroy : function() {
this._popModal();
if (this._keyPressEventId)
this.actor.disconnect(this._keyPressEventId);
if (this._keyReleaseEventId)
this.actor.disconnect(this._keyReleaseEventId);
},
_select : function(num) {
this._selection = num;
this._switcher.highlight(num);
}
});
const CtrlAltTabSwitcher = new Lang.Class({
Name: 'CtrlAltTabSwitcher',
Extends: AltTab.SwitcherList,
Extends: SwitcherPopup.SwitcherList,
_init : function(items) {
this.parent(true);

View File

@ -22,12 +22,8 @@ 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);
if (source instanceof AppDisplay.AppIcon) {
return source.app;
} else {
return null;
}
@ -37,30 +33,26 @@ function getAppFromSource(source) {
// when requesting a size
const DashItemContainer = new Lang.Class({
Name: 'DashItemContainer',
Extends: St.Widget,
_init: function() {
this.actor = new Shell.GenericContainer({ style_class: 'dash-item-container' });
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._delegate = this;
this.parent({ style_class: 'dash-item-container' });
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.label_actor = this.label;
this.child = null;
this._childScale = 1;
this._childOpacity = 255;
this._childScale = 0;
this._childOpacity = 0;
this.animatingOut = false;
},
_allocate: function(actor, box, flags) {
vfunc_allocate: function(box, flags) {
this.set_allocation(box, flags);
if (this.child == null)
return;
@ -82,28 +74,28 @@ const DashItemContainer = new Lang.Class({
this.child.allocate(childBox, flags);
},
_getPreferredHeight: function(actor, forWidth, alloc) {
alloc.min_size = 0;
alloc.natural_size = 0;
vfunc_get_preferred_height: function(forWidth) {
let themeNode = this.get_theme_node();
if (this.child == null)
return;
return [0, 0];
forWidth = themeNode.adjust_for_width(forWidth);
let [minHeight, natHeight] = this.child.get_preferred_height(forWidth);
alloc.min_size += minHeight * this.child.scale_y;
alloc.natural_size += natHeight * this.child.scale_y;
return themeNode.adjust_preferred_height(minHeight * this.child.scale_y,
natHeight * this.child.scale_y);
},
_getPreferredWidth: function(actor, forHeight, alloc) {
alloc.min_size = 0;
alloc.natural_size = 0;
vfunc_get_preferred_width: function(forHeight) {
let themeNode = this.get_theme_node();
if (this.child == null)
return;
return [0, 0];
forHeight = themeNode.adjust_for_height(forHeight);
let [minWidth, natWidth] = this.child.get_preferred_width(forHeight);
alloc.min_size = minWidth * this.child.scale_y;
alloc.natural_size = natWidth * this.child.scale_y;
return themeNode.adjust_preferred_width(minWidth * this.child.scale_y,
natWidth * this.child.scale_y);
},
showLabel: function() {
@ -114,9 +106,9 @@ const DashItemContainer = new Lang.Class({
this.label.opacity = 0;
this.label.show();
let [stageX, stageY] = this.actor.get_transformed_position();
let [stageX, stageY] = this.get_transformed_position();
let itemHeight = this.actor.allocation.y2 - this.actor.allocation.y1;
let itemHeight = this.allocation.y2 - this.allocation.y1;
let labelHeight = this.label.get_height();
let yOffset = Math.floor((itemHeight - labelHeight) / 2)
@ -130,7 +122,7 @@ const DashItemContainer = new Lang.Class({
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
x = stageX - this.label.get_width() - xOffset;
else
x = stageX + this.actor.get_width() + xOffset;
x = stageX + this.get_width() + xOffset;
this.label.set_position(x, y);
Tweener.addTween(this.label,
@ -142,6 +134,7 @@ const DashItemContainer = new Lang.Class({
setLabelText: function(text) {
this._labelText = text;
this.child.accessible_name = text;
},
hideLabel: function () {
@ -159,22 +152,25 @@ const DashItemContainer = new Lang.Class({
if (this.child == actor)
return;
this.actor.destroy_all_children();
this.destroy_all_children();
this.child = actor;
this.actor.add_actor(this.child);
this.add_actor(this.child);
this.child.set_scale_with_gravity(this._childScale, this._childScale,
Clutter.Gravity.CENTER);
this.child.set_opacity(this._childOpacity);
},
animateIn: function() {
show: function(animate) {
if (this.child == null)
return;
this.childScale = 0;
this.childOpacity = 0;
let time = animate ? DASH_ANIMATION_TIME : 0;
Tweener.addTween(this,
{ childScale: 1.0,
childOpacity: 255,
time: DASH_ANIMATION_TIME,
time: time,
transition: 'easeOutQuad'
});
},
@ -183,7 +179,7 @@ const DashItemContainer = new Lang.Class({
if (this.label)
this.label.destroy();
this.actor.destroy();
this.parent();
},
animateOutAndDestroy: function() {
@ -191,19 +187,18 @@ const DashItemContainer = new Lang.Class({
this.label.destroy();
if (this.child == null) {
this.actor.destroy();
this.destroy();
return;
}
this.animatingOut = true;
this.childScale = 1.0;
Tweener.addTween(this,
{ childScale: 0.0,
childOpacity: 0,
time: DASH_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.actor.destroy();
this.destroy();
})
});
},
@ -216,7 +211,7 @@ const DashItemContainer = new Lang.Class({
this.child.set_scale_with_gravity(scale, scale,
Clutter.Gravity.CENTER);
this.actor.queue_relayout();
this.queue_relayout();
},
get childScale() {
@ -230,7 +225,7 @@ const DashItemContainer = new Lang.Class({
return;
this.child.set_opacity(opacity);
this.actor.queue_redraw();
this.queue_redraw();
},
get childOpacity() {
@ -259,7 +254,6 @@ const ShowAppsIcon = new Lang.Class({
this.setChild(this.toggleButton);
this.setDragApp(null);
this.toggleButton.label_actor = this.label;
},
_createIcon: function(size) {
@ -362,6 +356,23 @@ const DashActor = new Lang.Class({
childBox.y1 = contentBox.y2 - showAppsNatHeight;
childBox.y2 = contentBox.y2;
showAppsButton.allocate(childBox, flags);
},
vfunc_get_preferred_height: function(forWidth) {
// We want to request the natural height of all our children
// as our natural height, so we chain up to StWidget (which
// then calls BoxLayout), but we only request the showApps
// button as the minimum size
let [, natHeight] = this.parent(forWidth);
let themeNode = this.get_theme_node();
let adjustedForWidth = themeNode.adjust_for_width(forWidth);
let [, showAppsButton] = this.get_children();
let [minHeight, ] = showAppsButton.get_preferred_height(adjustedForWidth);
[minHeight, ] = themeNode.adjust_preferred_height(minHeight, natHeight);
return [minHeight, natHeight];
}
});
@ -387,12 +398,14 @@ const Dash = new Lang.Class({
this._container.add_actor(this._box);
this._showAppsIcon = new ShowAppsIcon();
this._showAppsIcon.childScale = 1;
this._showAppsIcon.childOpacity = 255;
this._showAppsIcon.icon.setIconSize(this.iconSize);
this._hookUpLabel(this._showAppsIcon);
this.showAppsButton = this._showAppsIcon.toggleButton;
this._container.add_actor(this._showAppsIcon.actor);
this._container.add_actor(this._showAppsIcon);
this.actor = new St.Bin({ child: this._container });
this.actor.connect('notify::height', Lang.bind(this,
@ -416,12 +429,10 @@ const Dash = new Lang.Class({
Lang.bind(this, this._onDragEnd));
Main.overview.connect('item-drag-cancelled',
Lang.bind(this, this._onDragCancelled));
Main.overview.connect('window-drag-begin',
Lang.bind(this, this._onDragBegin));
Main.overview.connect('window-drag-cancelled',
Lang.bind(this, this._onDragCancelled));
Main.overview.connect('window-drag-end',
Lang.bind(this, this._onDragEnd));
// Translators: this is the name of the dock/favorites area on
// the left of the overview
Main.ctrlAltTabManager.addGroup(this.actor, _("Dash"), 'user-bookmarks-symbolic');
},
_onDragBegin: function() {
@ -456,7 +467,7 @@ const Dash = new Lang.Class({
return DND.DragMotionResult.CONTINUE;
let showAppsHovered =
this._showAppsIcon.actor.contains(dragEvent.targetActor);
this._showAppsIcon.contains(dragEvent.targetActor);
if (!this._box.contains(dragEvent.targetActor) || showAppsHovered)
this._clearDragPlaceholder();
@ -492,32 +503,51 @@ const Dash = new Lang.Class({
},
_createAppItem: function(app) {
let display = new AppDisplay.AppWellIcon(app,
{ setSizeManually: true,
showLabel: false });
display._draggable.connect('drag-begin',
let appIcon = new AppDisplay.AppIcon(app,
{ setSizeManually: true,
showLabel: false });
appIcon._draggable.connect('drag-begin',
Lang.bind(this, function() {
display.actor.opacity = 50;
appIcon.actor.opacity = 50;
}));
display._draggable.connect('drag-end',
appIcon._draggable.connect('drag-end',
Lang.bind(this, function() {
display.actor.opacity = 255;
appIcon.actor.opacity = 255;
}));
appIcon.connect('menu-state-changed',
Lang.bind(this, function(appIcon, opened) {
this._itemMenuStateChanged(item, opened);
}));
let item = new DashItemContainer();
item.setChild(display.actor);
item.setChild(appIcon.actor);
// Override default AppIcon label_actor, now the
// accessible_name is set at DashItemContainer.setLabelText
appIcon.actor.label_actor = null;
item.setLabelText(app.get_name());
// Override default AppWellIcon label_actor
display.actor.label_actor = item.label;
display.icon.setIconSize(this.iconSize);
appIcon.icon.setIconSize(this.iconSize);
this._hookUpLabel(item);
return item;
},
_itemMenuStateChanged: function(item, opened) {
// When the menu closes, it calls sync_hover, which means
// that the notify::hover handler does everything we need to.
if (opened) {
if (this._showLabelTimeoutId > 0) {
Mainloop.source_remove(this._showLabelTimeoutId);
this._showLabelTimeoutId = 0;
}
item.hideLabel();
}
},
_onHover: function (item) {
if (item.child.get_hover() && !item.child._delegate.isMenuUp) {
if (item.child.get_hover()) {
if (this._showLabelTimeoutId == 0) {
let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT;
this._showLabelTimeoutId = Mainloop.timeout_add(timeout,
@ -552,13 +582,13 @@ const Dash = new Lang.Class({
// animating out (which means they will be destroyed at the end of
// the animation)
let iconChildren = this._box.get_children().filter(function(actor) {
return actor._delegate.child &&
actor._delegate.child._delegate &&
actor._delegate.child._delegate.icon &&
!actor._delegate.animatingOut;
return actor.child &&
actor.child._delegate &&
actor.child._delegate.icon &&
!actor.animatingOut;
});
iconChildren.push(this._showAppsIcon.actor);
iconChildren.push(this._showAppsIcon);
if (this._maxHeight == -1)
return;
@ -571,23 +601,18 @@ const Dash = new Lang.Class({
let availHeight = maxContent.y2 - maxContent.y1;
let spacing = themeNode.get_length('spacing');
let firstIcon = iconChildren[0]._delegate.child._delegate.icon;
let firstButton = iconChildren[0].child;
let firstIcon = firstButton._delegate.icon;
let minHeight, natHeight;
// Enforce the current icon size during the size request if
// the icon is animating
if (firstIcon._animating) {
let [currentWidth, currentHeight] = firstIcon.icon.get_size();
// Enforce the current icon size during the size request
let [currentWidth, currentHeight] = firstIcon.icon.get_size();
firstIcon.icon.set_size(this.iconSize, this.iconSize);
[minHeight, natHeight] = iconChildren[0].get_preferred_height(-1);
firstIcon.icon.set_size(this.iconSize, this.iconSize);
[minHeight, natHeight] = firstButton.get_preferred_height(-1);
firstIcon.icon.set_size(currentWidth, currentHeight);
} else {
[minHeight, natHeight] = iconChildren[0].get_preferred_height(-1);
}
firstIcon.icon.set_size(currentWidth, currentHeight);
// Subtract icon padding and box spacing from the available height
availHeight -= iconChildren.length * (natHeight - this.iconSize) +
@ -612,15 +637,17 @@ const Dash = new Lang.Class({
let scale = oldIconSize / newIconSize;
for (let i = 0; i < iconChildren.length; i++) {
let icon = iconChildren[i]._delegate.child._delegate.icon;
let icon = iconChildren[i].child._delegate.icon;
// Set the new size immediately, to keep the icons' sizes
// in sync with this.iconSize
icon.setIconSize(this.iconSize);
// Don't animate the icon size change when the overview
// is not visible or when initially filling the dash
if (!Main.overview.visible || !this._shownInitially)
// is transitioning, not visible or when initially filling
// the dash
if (!Main.overview.visible || Main.overview.animationInProgress ||
!this._shownInitially)
continue;
let [targetWidth, targetHeight] = icon.icon.get_size();
@ -630,15 +657,11 @@ const Dash = new Lang.Class({
icon.icon.set_size(icon.icon.width * scale,
icon.icon.height * scale);
icon._animating = true;
Tweener.addTween(icon.icon,
{ width: targetWidth,
height: targetHeight,
time: DASH_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: function() {
icon._animating = false;
}
});
}
},
@ -649,13 +672,13 @@ const Dash = new Lang.Class({
let running = this._appSystem.get_running();
let children = this._box.get_children().filter(function(actor) {
return actor._delegate.child &&
actor._delegate.child._delegate &&
actor._delegate.child._delegate.app;
return actor.child &&
actor.child._delegate &&
actor.child._delegate.app;
});
// Apps currently in the dash
let oldApps = children.map(function(actor) {
return actor._delegate.child._delegate.app;
return actor.child._delegate.app;
});
// Apps supposed to be in the dash
let newApps = [];
@ -721,7 +744,7 @@ const Dash = new Lang.Class({
let insertHere = newApps[newIndex + 1] &&
newApps[newIndex + 1] == oldApps[oldIndex];
let alreadyRemoved = removedActors.reduce(function(result, actor) {
let removedApp = actor._delegate.child._delegate.app;
let removedApp = actor.child._delegate.app;
return result || removedApp == newApps[newIndex];
}, false);
@ -738,14 +761,15 @@ const Dash = new Lang.Class({
}
for (let i = 0; i < addedItems.length; i++)
this._box.insert_child_at_index(addedItems[i].item.actor,
this._box.insert_child_at_index(addedItems[i].item,
addedItems[i].pos);
for (let i = 0; i < removedActors.length; i++) {
let item = removedActors[i]._delegate;
let item = removedActors[i];
// Don't animate item removal when the overview is hidden
if (Main.overview.visible)
// Don't animate item removal when the overview is transitioning
// or hidden
if (Main.overview.visible && !Main.overview.animationInProgress)
item.animateOutAndDestroy();
else
item.destroy();
@ -755,17 +779,20 @@ const Dash = new Lang.Class({
// Skip animations on first run when adding the initial set
// of items, to avoid all items zooming in at once
if (!this._shownInitially) {
let animate = this._shownInitially && Main.overview.visible &&
!Main.overview.animationInProgress;
if (!this._shownInitially)
this._shownInitially = true;
return;
for (let i = 0; i < addedItems.length; i++) {
addedItems[i].item.show(animate);
}
// Don't animate item addition when the overview is hidden
if (!Main.overview.visible)
return;
for (let i = 0; i < addedItems.length; i++)
addedItems[i].item.animateIn();
// Workaround for https://bugzilla.gnome.org/show_bug.cgi?id=692744
// Without it, StBoxLayout may use a stale size cache
this._box.queue_relayout();
},
_clearDragPlaceholder: function() {
@ -796,7 +823,7 @@ const Dash = new Lang.Class({
// the remove target has the same size as "normal" items, we don't
// need to do the same adjustment there.
if (this._dragPlaceholder) {
boxHeight -= this._dragPlaceholder.actor.height;
boxHeight -= this._dragPlaceholder.height;
numChildren--;
}
@ -810,7 +837,7 @@ const Dash = new Lang.Class({
if (this._dragPlaceholder) {
this._dragPlaceholder.animateOutAndDestroy();
this._animatingPlaceholdersCount++;
this._dragPlaceholder.actor.connect('destroy',
this._dragPlaceholder.connect('destroy',
Lang.bind(this, function() {
this._animatingPlaceholdersCount--;
}));
@ -825,7 +852,7 @@ const Dash = new Lang.Class({
// an animation
let fadeIn;
if (this._dragPlaceholder) {
this._dragPlaceholder.actor.destroy();
this._dragPlaceholder.destroy();
fadeIn = false;
} else {
fadeIn = true;
@ -834,10 +861,9 @@ const Dash = new Lang.Class({
this._dragPlaceholder = new DragPlaceholderItem();
this._dragPlaceholder.child.set_width (this.iconSize);
this._dragPlaceholder.child.set_height (this.iconSize / 2);
this._box.insert_child_at_index(this._dragPlaceholder.actor,
this._box.insert_child_at_index(this._dragPlaceholder,
this._dragPlaceholderPos);
if (fadeIn)
this._dragPlaceholder.animateIn();
this._dragPlaceholder.show(fadeIn);
}
// Remove the drag placeholder if we are not in the
@ -875,10 +901,10 @@ const Dash = new Lang.Class({
let children = this._box.get_children();
for (let i = 0; i < this._dragPlaceholderPos; i++) {
if (this._dragPlaceholder &&
children[i] == this._dragPlaceholder.actor)
children[i] == this._dragPlaceholder)
continue;
let childId = children[i]._delegate.child._delegate.app.get_id();
let childId = children[i].child._delegate.app.get_id();
if (childId == id)
continue;
if (childId in favorites)

View File

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

View File

@ -136,9 +136,10 @@ const _Draggable = new Lang.Class({
},
_ungrabActor: function() {
Clutter.ungrab_pointer();
if (!this._onEventId)
return;
Clutter.ungrab_pointer();
this.actor.disconnect(this._onEventId);
this._onEventId = null;
},
@ -203,6 +204,19 @@ const _Draggable = new Lang.Class({
return false;
},
/**
* fakeRelease:
*
* Fake a release event.
* Must be called if you want to intercept release events on draggable
* actors for other purposes (for example if you're using
* PopupMenu.ignoreRelease())
*/
fakeRelease: function() {
this._buttonDown = false;
this._ungrabActor();
},
/**
* startDrag:
* @stageX: X coordinate of event
@ -234,7 +248,7 @@ const _Draggable = new Lang.Class({
this._dragY = this._dragStartY = stageY;
if (this.actor._delegate && this.actor._delegate.getDragActor) {
this._dragActor = this.actor._delegate.getDragActor(this._dragStartX, this._dragStartY);
this._dragActor = this.actor._delegate.getDragActor();
this._dragActor.reparent(Main.uiGroup);
this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true);

View File

@ -19,6 +19,7 @@
*/
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Signals = imports.signals;
const AccountsService = imports.gi.AccountsService;
@ -50,6 +51,7 @@ const EndSessionDialogIface = <interface name="org.gnome.SessionManager.EndSessi
<arg type="u" direction="in" />
<arg type="ao" direction="in" />
</method>
<method name="Close" />
<signal name="ConfirmedLogout" />
<signal name="ConfirmedReboot" />
<signal name="ConfirmedShutdown" />
@ -105,7 +107,7 @@ const restartDialogContent = {
endDescription: _("Restarting the system."),
confirmButtons: [{ signal: 'ConfirmedReboot',
label: C_("button", "Restart") }],
iconName: 'system-shutdown-symbolic',
iconName: 'view-refresh-symbolic',
iconStyleClass: 'end-session-dialog-shutdown-icon'
};
@ -377,7 +379,12 @@ const EndSessionDialog = new Lang.Class({
let signal = dialogContent.confirmButtons[i].signal;
let label = dialogContent.confirmButtons[i].label;
buttons.push({ action: Lang.bind(this, function() {
this._confirm(signal);
this.close(true);
let signalId = this.connect('closed',
Lang.bind(this, function() {
this.disconnect(signalId);
this._confirm(signal);
}));
}),
label: label });
}
@ -385,15 +392,17 @@ const EndSessionDialog = new Lang.Class({
this.setButtons(buttons);
},
close: function() {
close: function(skipSignal) {
this.parent();
this._dbusImpl.emit_signal('Closed', null);
if (!skipSignal)
this._dbusImpl.emit_signal('Closed', null);
},
cancel: function() {
this._stopTimer();
this._dbusImpl.emit_signal('Canceled', null);
this.close(global.get_current_time());
this.close();
},
_confirm: function(signal) {
@ -408,22 +417,34 @@ const EndSessionDialog = new Lang.Class({
},
_startTimer: function() {
let startTime = GLib.get_monotonic_time();
this._secondsLeft = this._totalSecondsToStayOpen;
Tweener.addTween(this,
{ _secondsLeft: 0,
time: this._secondsLeft,
transition: 'linear',
onUpdate: Lang.bind(this, this._updateDescription),
onComplete: Lang.bind(this, function() {
let dialogContent = DialogContent[this._type];
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
this._confirm(button.signal);
}),
});
this._timerId = Mainloop.timeout_add_seconds(1, Lang.bind(this,
function() {
let currentTime = GLib.get_monotonic_time();
let secondsElapsed = ((currentTime - startTime) / 1000000);
this._secondsLeft = this._totalSecondsToStayOpen - secondsElapsed;
if (this._secondsLeft > 0) {
this._updateDescription();
return true;
}
let dialogContent = DialogContent[this._type];
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
this._confirm(button.signal);
return false;
}));
},
_stopTimer: function() {
Tweener.removeTweens(this);
if (this._timerId != 0) {
Mainloop.source_remove(this._timerId);
this._timerId = 0;
}
this._secondsLeft = 0;
},
@ -440,7 +461,7 @@ const EndSessionDialog = new Lang.Class({
let item = new ListItem(app, reason);
item.connect('activate',
Lang.bind(this, function() {
this.close(global.get_current_time());
this.close();
}));
this._applicationList.add(item.actor, { x_fill: true });
this._stopTimer();
@ -488,5 +509,9 @@ const EndSessionDialog = new Lang.Class({
invocation.return_value(null);
this.disconnect(signalId);
}));
},
Close: function(parameters, invocation) {
this.close();
}
});

View File

@ -215,7 +215,7 @@ const InstallExtensionDialog = new Lang.Class({
},
_onCancelButtonPressed: function(button, event) {
this.close(global.get_current_time());
this.close();
this._invocation.return_value(GLib.Variant.new('(s)', ['cancelled']));
},
@ -257,7 +257,7 @@ const InstallExtensionDialog = new Lang.Class({
gotExtensionZipFile(session, message, uuid, dir, callback, errback);
}));
this.close(global.get_current_time());
this.close();
}
});

View File

@ -106,11 +106,15 @@ function enableExtension(uuid) {
extensionOrder.push(uuid);
let stylesheetFile = extension.dir.get_child('stylesheet.css');
if (stylesheetFile.query_exists(null)) {
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
theme.load_stylesheet(stylesheetFile.get_path());
extension.stylesheet = stylesheetFile;
let stylesheetNames = [global.session_mode + '.css', 'stylesheet.css'];
for (let i = 0; i < stylesheetNames.length; i++) {
let stylesheetFile = extension.dir.get_child(stylesheetNames[i]);
if (stylesheetFile.query_exists(null)) {
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
theme.load_stylesheet(stylesheetFile.get_path());
extension.stylesheet = stylesheetFile;
break;
}
}
extension.stateObj.enable();
@ -126,13 +130,10 @@ function logExtensionError(uuid, error) {
let message = '' + error;
if (error.state)
extension.state = error.state;
else
extension.state = ExtensionState.ERROR;
extension.state = ExtensionState.ERROR;
if (!extension.errors)
extension.errors = [];
extension.errors.push(message);
log('Extension "%s" had error: %s'.format(uuid, message));
_signals.emit('extension-state-changed', { uuid: uuid,
@ -145,18 +146,16 @@ function loadExtension(extension) {
extension.state = ExtensionState.ERROR;
if (ExtensionUtils.isOutOfDate(extension)) {
let error = new Error('extension is not compatible with current GNOME Shell and/or GJS version');
error.state = ExtensionState.OUT_OF_DATE;
throw error;
}
let enabled = enabledExtensions.indexOf(extension.uuid) != -1;
if (enabled) {
initExtension(extension.uuid);
if (extension.state == ExtensionState.DISABLED)
enableExtension(extension.uuid);
extension.state = ExtensionState.OUT_OF_DATE;
} else {
extension.state = ExtensionState.INITIALIZED;
let enabled = enabledExtensions.indexOf(extension.uuid) != -1;
if (enabled) {
initExtension(extension.uuid);
if (extension.state == ExtensionState.DISABLED)
enableExtension(extension.uuid);
} else {
extension.state = ExtensionState.INITIALIZED;
}
}
_signals.emit('extension-state-changed', extension);
@ -217,8 +216,16 @@ function initExtension(uuid) {
_signals.emit('extension-loaded', uuid);
}
function getEnabledExtensions() {
let extensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
if (!Array.isArray(Main.sessionMode.enabledExtensions))
return extensions;
return Main.sessionMode.enabledExtensions.concat(extensions);
}
function onEnabledExtensionsChanged() {
let newEnabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
let newEnabledExtensions = getEnabledExtensions();
if (!enabled)
return;
@ -252,7 +259,7 @@ function onEnabledExtensionsChanged() {
function _loadExtensions() {
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
enabledExtensions = getEnabledExtensions();
let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', function(signals, extension) {
@ -294,10 +301,17 @@ function disableAllExtensions() {
}
function _sessionUpdated() {
if (Main.sessionMode.allowExtensions)
// For now sessionMode.allowExtensions controls extensions from both the
// 'enabled-extensions' preference and the sessionMode.enabledExtensions
// property; it might make sense to make enabledExtensions independent
// from allowExtensions in the future
if (Main.sessionMode.allowExtensions) {
if (initted)
onEnabledExtensionsChanged();
enableAllExtensions();
else
} else {
disableAllExtensions();
}
}
function init() {

View File

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

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gtk = imports.gi.Gtk;
@ -10,19 +10,9 @@ const St = imports.gi.St;
const Main = imports.ui.main;
const Params = imports.misc.params;
function _navigateActor(actor) {
if (!actor)
return;
let needsGrab = true;
if (actor instanceof St.Widget)
needsGrab = !actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
if (needsGrab)
actor.grab_key_focus();
}
// GrabHelper:
// @owner: the actor that owns the GrabHelper
// @params: optional parameters to pass to Main.pushModal()
//
// Creates a new GrabHelper object, for dealing with keyboard and pointer grabs
// associated with a set of actors.
@ -34,17 +24,18 @@ function _navigateActor(actor) {
const GrabHelper = new Lang.Class({
Name: 'GrabHelper',
_init: function(owner) {
_init: function(owner, params) {
this._owner = owner;
this._modalParams = params;
this._grabStack = [];
this._actors = [];
this._capturedEventId = 0;
this._eventId = 0;
this._keyFocusNotifyId = 0;
this._focusWindowChangedId = 0;
this._ignoreRelease = false;
this._isUngrabbingCount = 0;
this._modalCount = 0;
this._grabFocusCount = 0;
@ -76,9 +67,12 @@ const GrabHelper = new Lang.Class({
},
_isWithinGrabbedActor: function(actor) {
let currentActor = this.currentGrab.actor;
while (actor) {
if (this._actors.indexOf(actor) != -1)
return true;
if (actor == currentActor)
return true;
actor = actor.get_parent();
}
return false;
@ -88,6 +82,14 @@ const GrabHelper = new Lang.Class({
return this._grabStack[this._grabStack.length - 1] || {};
},
get grabbed() {
return this._grabStack.length > 0;
},
get grabStack() {
return this._grabStack;
},
_findStackIndex: function(actor) {
if (!actor)
return -1;
@ -99,6 +101,16 @@ const GrabHelper = new Lang.Class({
return -1;
},
_actorInGrabStack: function(actor) {
while (actor) {
let idx = this._findStackIndex(actor);
if (idx >= 0)
return idx;
actor = actor.get_parent();
}
return -1;
},
isActorGrabbed: function(actor) {
return this._findStackIndex(actor) >= 0;
},
@ -138,6 +150,7 @@ const GrabHelper = new Lang.Class({
params = Params.parse(params, { actor: null,
modal: false,
grabFocus: false,
focus: null,
onUngrab: null });
let focus = global.stage.key_focus;
@ -155,21 +168,26 @@ const GrabHelper = new Lang.Class({
if (params.grabFocus && !this._takeFocusGrab(hadFocus))
return false;
if (hadFocus || params.grabFocus)
_navigateActor(newFocus);
this._grabStack.push(params);
if (params.focus) {
params.focus.grab_key_focus();
} else if (newFocus && (hadFocus || params.grabFocus)) {
if (!newFocus.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false))
newFocus.grab_key_focus();
}
if ((params.grabFocus || params.modal) && !this._capturedEventId)
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
return true;
},
_takeModalGrab: function() {
let firstGrab = (this._modalCount == 0);
if (firstGrab) {
if (!Main.pushModal(this._owner))
if (!Main.pushModal(this._owner, this._modalParams))
return false;
this._capturedEventId = global.stage.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
this._eventId = global.stage.connect('event', Lang.bind(this, this._onEvent));
}
this._modalCount++;
@ -181,16 +199,6 @@ const GrabHelper = new Lang.Class({
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();
},
@ -202,7 +210,6 @@ const GrabHelper = new Lang.Class({
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) {
@ -227,7 +234,7 @@ const GrabHelper = new Lang.Class({
this._keyFocusNotifyId = 0;
}
if (!this._focusWindowChanged > 0) {
if (this._focusWindowChangedId > 0) {
let metaDisplay = global.screen.get_display();
metaDisplay.disconnect(this._focusWindowChangedId);
this._focusWindowChangedId = 0;
@ -241,13 +248,7 @@ const GrabHelper = new Lang.Class({
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());
}
}
global.screen.focus_default_window(global.get_current_time());
},
// ignoreRelease:
@ -268,12 +269,21 @@ const GrabHelper = new Lang.Class({
// If the actor that was popped from the grab stack was not the actor
// That was passed in, this call is ignored.
ungrab: function(params) {
params = Params.parse(params, { actor: this.currentGrab.actor });
params = Params.parse(params, { actor: this.currentGrab.actor,
isUser: false });
let grabStackIndex = this._findStackIndex(params.actor);
if (grabStackIndex < 0)
return;
// We may get key focus changes when calling onUngrab, which
// would cause an extra ungrab() on the next actor in the
// stack, which is wrong. Ignore key focus changes during the
// ungrab, and restore the saved key focus ourselves afterwards.
// We use a count as ungrab() may be re-entrant, as onUngrab()
// may ungrab additional actors.
this._isUngrabbingCount++;
let focus = global.stage.key_focus;
let hadFocus = focus && this._isWithinGrabbedActor(focus);
@ -286,7 +296,7 @@ const GrabHelper = new Lang.Class({
let poppedGrab = poppedGrabs[i];
if (poppedGrab.onUngrab)
poppedGrab.onUngrab();
poppedGrab.onUngrab(params.isUser);
if (poppedGrab.modal)
this._releaseModalGrab();
@ -295,21 +305,36 @@ const GrabHelper = new Lang.Class({
this._releaseFocusGrab();
}
if (!this.grabbed && this._capturedEventId > 0) {
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
}
if (hadFocus) {
let poppedGrab = poppedGrabs[0];
_navigateActor(poppedGrab.savedFocus);
if (poppedGrab.savedFocus)
poppedGrab.savedFocus.grab_key_focus();
}
this._isUngrabbingCount--;
},
_onCapturedEvent: function(actor, event) {
let type = event.type();
if (type == Clutter.EventType.KEY_PRESS &&
event.get_key_symbol() == Clutter.KEY_Escape) {
this.ungrab({ isUser: true });
return true;
}
let press = type == Clutter.EventType.BUTTON_PRESS;
let release = type == Clutter.EventType.BUTTON_RELEASE;
let button = press || release;
if (release && this._ignoreRelease) {
this._ignoreRelease = false;
return false;
return true;
}
if (!button && this._modalCount == 0)
@ -326,33 +351,25 @@ const GrabHelper = new Lang.Class({
// which should be a release event.
if (press)
this._ignoreRelease = true;
this.ungrab({ actor: this._grabStack[0].actor });
let i = this._actorInGrabStack(event.get_source()) + 1;
this.ungrab({ actor: this._grabStack[i].actor, isUser: true });
}
return this._modalCount > 0;
},
// We catch 'event' rather than 'key-press-event' so that we get
// a chance to run before the overview's own Escape check
_onEvent: function(actor, event) {
if (event.type() == Clutter.EventType.KEY_PRESS &&
event.get_key_symbol() == Clutter.KEY_Escape) {
this.ungrab();
return true;
}
return false;
},
_onKeyFocusChanged: function() {
if (this._isUngrabbingCount > 0)
return;
let focus = global.stage.key_focus;
if (!focus || !this._isWithinGrabbedActor(focus))
this.ungrab();
this.ungrab({ isUser: true });
},
_focusWindowChanged: function() {
let metaDisplay = global.screen.get_display();
if (metaDisplay.focus_window != null)
this.ungrab();
this.ungrab({ isUser: true });
}
});

View File

@ -3,107 +3,149 @@
const Clutter = imports.gi.Clutter;
const IBus = imports.gi.IBus;
const Lang = imports.lang;
const Signals = imports.signals;
const St = imports.gi.St;
const BoxPointer = imports.ui.boxpointer;
const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const MAX_CANDIDATES_PER_PAGE = 16;
const CandidateArea = new Lang.Class({
Name: 'CandidateArea',
Extends: PopupMenu.PopupBaseMenuItem,
_init: function() {
this.parent({ reactive: false });
// St.Table exhibits some sizing problems so let's go with a
// clutter layout manager for now.
this._table = new Clutter.Actor();
this.addActor(this._table);
this._tableLayout = new Clutter.TableLayout();
this._table.set_layout_manager(this._tableLayout);
this._indexLabels = [];
this._candidateLabels = [];
this.actor = new St.BoxLayout({ vertical: true,
visible: false });
this._candidateBoxes = [];
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
this._indexLabels.push(new St.Label({ style_class: 'candidate-index' }));
this._candidateLabels.push(new St.Label({ style_class: 'candidate-label' }));
let box = new St.BoxLayout({ style_class: 'candidate-box',
reactive: true,
track_hover: true });
box._indexLabel = new St.Label({ style_class: 'candidate-index' });
box._candidateLabel = new St.Label({ style_class: 'candidate-label' });
box.add(box._indexLabel, { y_fill: false });
box.add(box._candidateLabel, { y_fill: false });
this._candidateBoxes.push(box);
this.actor.add(box);
let j = i;
box.connect('button-release-event', Lang.bind(this, function(actor, event) {
this.emit('candidate-clicked', j, event.get_button(), event.get_state());
}));
}
this._buttonBox = new St.BoxLayout({ style_class: 'candidate-page-button-box' });
this._previousButton = new St.Button({ style_class: 'candidate-page-button candidate-page-button-previous' });
this._previousButton.child = new St.Icon({ style_class: 'candidate-page-button-icon' });
this._buttonBox.add(this._previousButton, { expand: true });
this._nextButton = new St.Button({ style_class: 'candidate-page-button candidate-page-button-next' });
this._nextButton.child = new St.Icon({ style_class: 'candidate-page-button-icon' });
this._buttonBox.add(this._nextButton, { expand: true });
this.actor.add(this._buttonBox);
this._previousButton.connect('clicked', Lang.bind(this, function() {
this.emit('previous-page');
}));
this._nextButton.connect('clicked', Lang.bind(this, function() {
this.emit('next-page');
}));
this._orientation = -1;
this._cursorPosition = 0;
},
_setOrientation: function(orientation) {
setOrientation: function(orientation) {
if (this._orientation == orientation)
return;
this._orientation = orientation;
this._table.remove_all_children();
if (this._orientation == IBus.Orientation.HORIZONTAL)
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
this._tableLayout.pack(this._indexLabels[i], i*2, 0);
this._tableLayout.pack(this._candidateLabels[i], i*2 + 1, 0);
}
else // VERTICAL || SYSTEM
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
this._tableLayout.pack(this._indexLabels[i], 0, i);
this._tableLayout.pack(this._candidateLabels[i], 1, i);
}
if (this._orientation == IBus.Orientation.HORIZONTAL) {
this.actor.vertical = false;
this.actor.remove_style_class_name('vertical');
this.actor.add_style_class_name('horizontal');
this._previousButton.child.icon_name = 'go-previous-symbolic';
this._nextButton.child.icon_name = 'go-next-symbolic';
} else { // VERTICAL || SYSTEM
this.actor.vertical = true;
this.actor.add_style_class_name('vertical');
this.actor.remove_style_class_name('horizontal');
this._previousButton.child.icon_name = 'go-up-symbolic';
this._nextButton.child.icon_name = 'go-down-symbolic';
}
},
setCandidates: function(indexes, candidates, orientation, cursorPosition, cursorVisible) {
this._setOrientation(orientation);
setCandidates: function(indexes, candidates, cursorPosition, cursorVisible) {
for (let i = 0; i < MAX_CANDIDATES_PER_PAGE; ++i) {
let visible = i < candidates.length;
this._indexLabels[i].visible = visible;
this._candidateLabels[i].visible = visible;
let box = this._candidateBoxes[i];
box.visible = visible;
if (!visible)
continue;
this._indexLabels[i].text = ((indexes && indexes[i]) ? indexes[i] : '%x.'.format(i + 1));
this._candidateLabels[i].text = candidates[i];
box._indexLabel.text = ((indexes && indexes[i]) ? indexes[i] : '%x'.format(i + 1));
box._candidateLabel.text = candidates[i];
}
this._candidateLabels[this._cursorPosition].remove_style_pseudo_class('selected');
this._candidateBoxes[this._cursorPosition].remove_style_pseudo_class('selected');
this._cursorPosition = cursorPosition;
if (cursorVisible)
this._candidateLabels[cursorPosition].add_style_pseudo_class('selected');
this._candidateBoxes[cursorPosition].add_style_pseudo_class('selected');
},
updateButtons: function(wrapsAround, page, nPages) {
if (nPages < 2) {
this._buttonBox.hide();
return;
}
this._buttonBox.show();
this._previousButton.reactive = wrapsAround || page > 0;
this._nextButton.reactive = wrapsAround || page < nPages - 1;
},
});
Signals.addSignalMethods(CandidateArea.prototype);
const CandidatePopup = new Lang.Class({
Name: 'CandidatePopup',
Extends: PopupMenu.PopupMenu,
_init: function() {
this._cursor = new St.Bin({ opacity: 0 });
Main.uiGroup.add_actor(this._cursor);
this.parent(this._cursor, 0, St.Side.TOP);
this.actor.hide();
Main.uiGroup.add_actor(this.actor);
this._boxPointer = new BoxPointer.BoxPointer(St.Side.TOP);
this._boxPointer.actor.visible = false;
this._boxPointer.actor.style_class = 'candidate-popup-boxpointer';
Main.layoutManager.addChrome(this._boxPointer.actor);
this._preeditTextItem = new PopupMenu.PopupMenuItem('', { reactive: false });
this._preeditTextItem.actor.hide();
this.addMenuItem(this._preeditTextItem);
let box = new St.BoxLayout({ style_class: 'candidate-popup-content',
vertical: true });
this._boxPointer.bin.set_child(box);
this._auxTextItem = new PopupMenu.PopupMenuItem('', { reactive: false });
this._auxTextItem.actor.hide();
this.addMenuItem(this._auxTextItem);
this._preeditText = new St.Label({ style_class: 'candidate-popup-text',
visible: false });
box.add(this._preeditText);
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._auxText = new St.Label({ style_class: 'candidate-popup-text',
visible: false });
box.add(this._auxText);
this._lookupTableItem = new CandidateArea();
this._lookupTableItem.actor.hide();
this.addMenuItem(this._lookupTableItem);
this._candidateArea = new CandidateArea();
box.add(this._candidateArea.actor);
this._candidateArea.connect('previous-page', Lang.bind(this, function() {
this._panelService.page_up();
}));
this._candidateArea.connect('next-page', Lang.bind(this, function() {
this._panelService.page_down();
}));
this._candidateArea.connect('candidate-clicked', Lang.bind(this, function(ca, index, button, state) {
this._panelService.candidate_clicked(index, button, state);
}));
this._panelService = null;
},
@ -117,66 +159,61 @@ const CandidatePopup = new Lang.Class({
Lang.bind(this, function(ps, x, y, w, h) {
this._cursor.set_position(x, y);
this._cursor.set_size(w, h);
if (this._boxPointer.actor.visible)
this._boxPointer.setPosition(this._cursor, 0);
}));
panelService.connect('update-preedit-text',
Lang.bind(this, function(ps, text, cursorPosition, visible) {
if (visible)
this._preeditTextItem.actor.show();
else
this._preeditTextItem.actor.hide();
this._preeditText.visible = visible;
this._updateVisibility();
this._preeditTextItem.actor.label_actor.text = text.get_text();
this._preeditText.text = text.get_text();
let attrs = text.get_attributes();
if (attrs)
this._setTextAttributes(this._preeditTextItem.actor.label_actor.clutter_text,
this._setTextAttributes(this._preeditText.clutter_text,
attrs);
}));
panelService.connect('show-preedit-text',
Lang.bind(this, function(ps) {
this._preeditTextItem.actor.show();
this._preeditText.show();
this._updateVisibility();
}));
panelService.connect('hide-preedit-text',
Lang.bind(this, function(ps) {
this._preeditTextItem.actor.hide();
this._preeditText.hide();
this._updateVisibility();
}));
panelService.connect('update-auxiliary-text',
Lang.bind(this, function(ps, text, visible) {
if (visible)
this._auxTextItem.actor.show();
else
this._auxTextItem.actor.hide();
this._auxText.visible = visible;
this._updateVisibility();
this._auxTextItem.actor.label_actor.text = text.get_text();
this._auxText.text = text.get_text();
}));
panelService.connect('show-auxiliary-text',
Lang.bind(this, function(ps) {
this._auxTextItem.actor.show();
this._auxText.show();
this._updateVisibility();
}));
panelService.connect('hide-auxiliary-text',
Lang.bind(this, function(ps) {
this._auxTextItem.actor.hide();
this._auxText.hide();
this._updateVisibility();
}));
panelService.connect('update-lookup-table',
Lang.bind(this, function(ps, lookupTable, visible) {
if (visible)
this._lookupTableItem.actor.show();
else
this._lookupTableItem.actor.hide();
this._candidateArea.actor.visible = visible;
this._updateVisibility();
let nCandidates = lookupTable.get_number_of_candidates();
let cursorPos = lookupTable.get_cursor_pos();
let pageSize = lookupTable.get_page_size();
let nPages = Math.ceil(nCandidates / pageSize);
let page = ((cursorPos == 0) ? 0 : Math.floor(cursorPos / pageSize));
let startIndex = page * pageSize;
let endIndex = Math.min((page + 1) * pageSize,
lookupTable.get_number_of_candidates());
let endIndex = Math.min((page + 1) * pageSize, nCandidates);
let indexes = [];
let indexLabel;
for (let i = 0; indexLabel = lookupTable.get_label(i); ++i)
@ -186,37 +223,41 @@ const CandidatePopup = new Lang.Class({
for (let i = startIndex; i < endIndex; ++i)
candidates.push(lookupTable.get_candidate(i).get_text());
this._lookupTableItem.setCandidates(indexes,
candidates,
lookupTable.get_orientation(),
cursorPos % pageSize,
lookupTable.is_cursor_visible());
this._candidateArea.setCandidates(indexes,
candidates,
cursorPos % pageSize,
lookupTable.is_cursor_visible());
this._candidateArea.setOrientation(lookupTable.get_orientation());
this._candidateArea.updateButtons(lookupTable.is_round(), page, nPages);
}));
panelService.connect('show-lookup-table',
Lang.bind(this, function(ps) {
this._lookupTableItem.actor.show();
this._candidateArea.actor.show();
this._updateVisibility();
}));
panelService.connect('hide-lookup-table',
Lang.bind(this, function(ps) {
this._lookupTableItem.actor.hide();
this._candidateArea.actor.hide();
this._updateVisibility();
}));
panelService.connect('focus-out',
Lang.bind(this, function(ps) {
this.close(BoxPointer.PopupAnimation.NONE);
this._boxPointer.hide(BoxPointer.PopupAnimation.NONE);
}));
},
_updateVisibility: function() {
let isVisible = (this._preeditTextItem.actor.visible ||
this._auxTextItem.actor.visible ||
this._lookupTableItem.actor.visible);
let isVisible = (this._preeditText.visible ||
this._auxText.visible ||
this._candidateArea.actor.visible);
if (isVisible)
this.open(BoxPointer.PopupAnimation.NONE);
else
this.close(BoxPointer.PopupAnimation.NONE);
if (isVisible) {
this._boxPointer.setPosition(this._cursor, 0);
this._boxPointer.show(BoxPointer.PopupAnimation.NONE);
this._boxPointer.actor.raise_top();
} else {
this._boxPointer.hide(BoxPointer.PopupAnimation.NONE);
}
},
_setTextAttributes: function(clutterText, ibusAttrList) {

View File

@ -23,6 +23,8 @@ const BaseIcon = new Lang.Class({
this.actor._delegate = this;
this.actor.connect('style-changed',
Lang.bind(this, this._onStyleChanged));
this.actor.connect('destroy',
Lang.bind(this, this._onDestroy));
this._spacing = 0;
@ -52,6 +54,9 @@ const BaseIcon = new Lang.Class({
this._setSizeManually = params.setSizeManually;
this.icon = null;
let cache = St.TextureCache.get_default();
this._iconThemeChangedId = cache.connect('icon-theme-changed', Lang.bind(this, this._onIconThemeChanged));
},
_allocate: function(actor, box, flags) {
@ -146,7 +151,22 @@ const BaseIcon = new Lang.Class({
size = found ? len : ICON_SIZE;
}
if (this.iconSize == size && this._iconBin.child)
return;
this._createIconTexture(size);
},
_onDestroy: function() {
if (this._iconThemeChangedId > 0) {
let cache = St.TextureCache.get_default();
cache.disconnect(this._iconThemeChangedId);
this._iconThemeChangedId = 0;
}
},
_onIconThemeChanged: function() {
this._createIconTexture(this.iconSize);
}
});
@ -156,13 +176,16 @@ const IconGrid = new Lang.Class({
_init: function(params) {
params = Params.parse(params, { rowLimit: null,
columnLimit: null,
fillParent: false,
xAlign: St.Align.MIDDLE });
this._rowLimit = params.rowLimit;
this._colLimit = params.columnLimit;
this._xAlign = params.xAlign;
this._fillParent = params.fillParent;
this.actor = new St.BoxLayout({ style_class: 'icon-grid',
vertical: true });
// Pulled from CSS, but hardcode some defaults here
this._spacing = 0;
this._hItemSize = this._vItemSize = ICON_SIZE;
@ -176,6 +199,11 @@ const IconGrid = new Lang.Class({
},
_getPreferredWidth: function (grid, forHeight, alloc) {
if (this._fillParent)
// Ignore all size requests of children and request a size of 0;
// later we'll allocate as many children as fit the parent
return;
let children = this._grid.get_children();
let nColumns = this._colLimit ? Math.min(this._colLimit,
children.length)
@ -197,8 +225,20 @@ const IconGrid = new Lang.Class({
},
_getPreferredHeight: function (grid, forWidth, alloc) {
if (this._fillParent)
// Ignore all size requests of children and request a size of 0;
// later we'll allocate as many children as fit the parent
return;
let children = this._getVisibleChildren();
let [nColumns, usedWidth] = this._computeLayout(forWidth);
let nColumns, spacing;
if (forWidth < 0) {
nColumns = children.length;
spacing = this._spacing;
} else {
[nColumns, , spacing] = this._computeLayout(forWidth);
}
let nRows;
if (nColumns > 0)
nRows = Math.ceil(children.length / nColumns);
@ -206,18 +246,25 @@ const IconGrid = new Lang.Class({
nRows = 0;
if (this._rowLimit)
nRows = Math.min(nRows, this._rowLimit);
let totalSpacing = Math.max(0, nRows - 1) * this._spacing;
let totalSpacing = Math.max(0, nRows - 1) * spacing;
let height = nRows * this._vItemSize + totalSpacing;
alloc.min_size = height;
alloc.natural_size = height;
},
_allocate: function (grid, box, flags) {
if (this._fillParent) {
// Reset the passed in box to fill the parent
let parentBox = this.actor.get_parent().allocation;
let gridBox = this.actor.get_theme_node().get_content_box(parentBox);
box = this._grid.get_theme_node().get_content_box(gridBox);
}
let children = this._getVisibleChildren();
let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1;
let [nColumns, usedWidth] = this._computeLayout(availWidth);
let [nColumns, usedWidth, spacing] = this._computeLayout(availWidth);
let leftPadding;
switch(this._xAlign) {
@ -256,7 +303,8 @@ const IconGrid = new Lang.Class({
childBox.x2 = childBox.x1 + width;
childBox.y2 = childBox.y1 + height;
if (this._rowLimit && rowIndex >= this._rowLimit) {
if (this._rowLimit && rowIndex >= this._rowLimit ||
this._fillParent && childBox.y2 > availHeight) {
this._grid.set_skip_paint(children[i], true);
} else {
children[i].allocate(childBox, flags);
@ -270,10 +318,10 @@ const IconGrid = new Lang.Class({
}
if (columnIndex == 0) {
y += this._vItemSize + this._spacing;
y += this._vItemSize + spacing;
x = box.x1 + leftPadding;
} else {
x += this._hItemSize + this._spacing;
x += this._hItemSize + spacing;
}
}
},
@ -289,16 +337,25 @@ const IconGrid = new Lang.Class({
_computeLayout: function (forWidth) {
let nColumns = 0;
let usedWidth = 0;
let spacing = this._spacing;
if (this._colLimit) {
let itemWidth = this._hItemSize * this._colLimit;
let emptyArea = forWidth - itemWidth;
spacing = Math.max(this._spacing, emptyArea / (2 * this._colLimit));
spacing = Math.round(spacing);
}
while ((this._colLimit == null || nColumns < this._colLimit) &&
(usedWidth + this._hItemSize <= forWidth)) {
usedWidth += this._hItemSize + this._spacing;
usedWidth += this._hItemSize + spacing;
nColumns += 1;
}
if (nColumns > 0)
usedWidth -= this._spacing;
usedWidth -= spacing;
return [nColumns, usedWidth];
return [nColumns, usedWidth, spacing];
},
_onStyleChanged: function() {

View File

@ -2,43 +2,27 @@
const Caribou = imports.gi.Caribou;
const Clutter = imports.gi.Clutter;
const DBus = imports.dbus;
const Gdk = imports.gi.Gdk;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const BoxPointer = imports.ui.boxpointer;
const Layout = imports.ui.layout;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const KEYBOARD_REST_TIME = Layout.KEYBOARD_ANIMATION_TIME * 2 * 1000;
const KEYBOARD_SCHEMA = 'org.gnome.shell.keyboard';
const KEYBOARD_TYPE = 'keyboard-type';
const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
const SHOW_KEYBOARD = 'screen-keyboard-enabled';
// Key constants taken from Antler
// FIXME: ought to be moved into libcaribou
const PRETTY_KEYS = {
'BackSpace': '\u232b',
'space': ' ',
'Return': '\u23ce',
'Caribou_Prefs': '\u2328',
'Caribou_ShiftUp': '\u2b06',
'Caribou_ShiftDown': '\u2b07',
'Caribou_Emoticons': '\u263a',
'Caribou_Symbols': '123',
'Caribou_Symbols_More': '{#*',
'Caribou_Alpha': 'Abc',
'Tab': 'Tab',
'Escape': 'Esc',
'Control_L': 'Ctrl',
'Alt_L': 'Alt'
};
const CaribouKeyboardIface = <interface name='org.gnome.Caribou.Keyboard'>
<method name='Show'>
<arg type='u' direction='in' />
@ -75,14 +59,7 @@ const Key = new Lang.Class({
if (this._key.name == 'Control_L' || this._key.name == 'Alt_L')
this._key.latch = true;
this._key.connect('key-pressed', Lang.bind(this, function ()
{ this.actor.checked = true }));
this._key.connect('key-released', Lang.bind(this, function ()
{ this.actor.checked = false; }));
if (this._extended_keys.length > 0) {
this._grabbed = false;
this._eventCaptureId = 0;
this._key.connect('notify::show-subkeys', Lang.bind(this, this._onShowSubkeysChanged));
this._boxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
{ x_fill: true,
@ -98,17 +75,7 @@ const Key = new Lang.Class({
},
_makeKey: function () {
let label = this._key.name;
if (label.length > 1) {
let pretty = PRETTY_KEYS[label];
if (pretty)
label = pretty;
else
label = this._getUnichar(this._key);
}
label = GLib.markup_escape_text(label, -1);
let label = GLib.markup_escape_text(this._key.label, -1);
let button = new St.Button ({ label: label,
style_class: 'keyboard-key' });
@ -144,52 +111,23 @@ const Key = new Lang.Class({
this._boxPointer.bin.add_actor(this._extended_keyboard);
},
_onEventCapture: function (actor, event) {
let source = event.get_source();
let type = event.type();
if ((type == Clutter.EventType.BUTTON_PRESS ||
type == Clutter.EventType.BUTTON_RELEASE) &&
this._extended_keyboard.contains(source)) {
source.extended_key.press();
source.extended_key.release();
return false;
}
if (type == Clutter.EventType.BUTTON_PRESS) {
this._boxPointer.actor.hide();
this._ungrab();
return true;
}
return false;
},
_ungrab: function () {
global.stage.disconnect(this._eventCaptureId);
this._eventCaptureId = 0;
this._grabbed = false;
Main.popModal(this.actor);
get subkeys() {
return this._boxPointer;
},
_onShowSubkeysChanged: function () {
if (this._key.show_subkeys) {
this.actor.fake_release();
this._boxPointer.actor.raise_top();
this._boxPointer.setPosition(this.actor, 0.5);
this._boxPointer.show(BoxPointer.PopupAnimation.FULL);
this.emit('show-subkeys');
this.actor.fake_release();
this.actor.set_hover(false);
if (!this._grabbed) {
Main.pushModal(this.actor);
this._eventCaptureId = global.stage.connect('captured-event', Lang.bind(this, this._onEventCapture));
this._grabbed = true;
}
this._key.release();
} else {
if (this._grabbed)
this._ungrab();
this._boxPointer.hide(BoxPointer.PopupAnimation.FULL);
this.emit('hide-subkeys');
}
}
});
Signals.addSignalMethods(Key.prototype);
const Keyboard = new Lang.Class({
// HACK: we can't set Name, because it collides with Name dbus property
@ -204,16 +142,26 @@ const Keyboard = new Lang.Class({
this._focusInExtendedKeys = false;
this._timestamp = global.display.get_current_time_roundtrip();
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
this._keyboardSettings.connect('changed', Lang.bind(this, this._settingsChanged));
this._a11yApplicationsSettings = new Gio.Settings({ schema: A11Y_APPLICATIONS_SCHEMA });
this._a11yApplicationsSettings.connect('changed', Lang.bind(this, this._settingsChanged));
this._settingsChanged();
},
init: function () {
this._showIdleId = 0;
this._subkeysBoxPointer = null;
this._capturedEventId = 0;
this._capturedPress = false;
this._keyboardVisible = false;
Main.layoutManager.connect('keyboard-visible-changed', Lang.bind(this, function(o, visible) {
this._keyboardVisible = visible;
}));
this._keyboardRequested = false;
this._keyboardRestingId = 0;
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
this._redraw();
},
@ -228,25 +176,19 @@ const Keyboard = new Lang.Class({
if (this._keyboard)
this._destroyKeyboard();
if (this._enableKeyboard) {
// If we've been called because the setting actually just
// changed to true (as opposed to being called from
// this._init()), then we want to pop up the keyboard.
let showKeyboard = (settings != null);
// However, caribou-gtk-module or this._onKeyFocusChanged
// will probably immediately tell us to hide it, so we
// have to fake things out so we'll ignore that request.
if (showKeyboard)
this._timestamp = global.display.get_current_time_roundtrip() + 1;
this._setupKeyboard(showKeyboard);
} else
if (this._enableKeyboard)
this._setupKeyboard();
else
Main.layoutManager.hideKeyboard(true);
},
_destroyKeyboard: function() {
if (this._keyboardNotifyId)
this._keyboard.disconnect(this._keyboardNotifyId);
if (this._keyboardGroupAddedId)
this._keyboard.disconnect(this._keyboardGroupAddedId);
if (this._keyboardGroupRemovedId)
this._keyboard.disconnect(this._keyboardGroupRemovedId);
if (this._focusNotifyId)
global.stage.disconnect(this._focusNotifyId);
this._keyboard = null;
@ -256,7 +198,7 @@ const Keyboard = new Lang.Class({
this._destroySource();
},
_setupKeyboard: function(show) {
_setupKeyboard: function() {
this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true, reactive: true });
Main.layoutManager.keyboardBox.add_actor(this.actor);
Main.layoutManager.trackChrome(this.actor);
@ -277,12 +219,11 @@ const Keyboard = new Lang.Class({
this.actor.text_direction = Clutter.TextDirection.LTR;
this._keyboardNotifyId = this._keyboard.connect('notify::active-group', Lang.bind(this, this._onGroupChanged));
this._keyboardGroupAddedId = this._keyboard.connect('group-added', Lang.bind(this, this._onGroupAdded));
this._keyboardGroupRemovedId = this._keyboard.connect('group-removed', Lang.bind(this, this._onGroupRemoved));
this._focusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
if (show)
this.show();
else
this._createSource();
this._createSource();
},
_onKeyFocusChanged: function () {
@ -302,88 +243,102 @@ const Keyboard = new Lang.Class({
return;
let time = global.get_current_time();
if (focus instanceof Clutter.Text)
this.Show(time);
else
if (!(focus instanceof Clutter.Text)) {
this.Hide(time);
return;
}
if (!this._showIdleId)
this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE,
Lang.bind(this, function() { this.Show(time); }));
},
_createLayersForGroup: function (gname) {
let group = this._keyboard.get_group(gname);
group.connect('notify::active-level', Lang.bind(this, this._onLevelChanged));
let layers = {};
let levels = group.get_levels();
for (let j = 0; j < levels.length; ++j) {
let lname = levels[j];
let level = group.get_level(lname);
let layout = new St.BoxLayout({ style_class: 'keyboard-layout',
vertical: true });
this._loadRows(level, layout);
layers[lname] = layout;
this.actor.add(layout, { x_fill: false });
layout.hide();
}
return layers;
},
_addKeys: function () {
let groups = this._keyboard.get_groups();
for (let i = 0; i < groups.length; ++i) {
let gname = groups[i];
let group = this._keyboard.get_group(gname);
group.connect('notify::active-level', Lang.bind(this, this._onLevelChanged));
let layers = {};
let levels = group.get_levels();
for (let j = 0; j < levels.length; ++j) {
let lname = levels[j];
let level = group.get_level(lname);
let layout = new St.BoxLayout({ style_class: 'keyboard-layout',
vertical: true });
this._loadRows(level, layout);
layers[lname] = layout;
this.actor.add(layout, { x_fill: false });
layout.hide();
}
this._groups[gname] = layers;
this._groups[gname] = this._createLayersForGroup(gname);
}
this._setActiveLayer();
},
_getTrayIcon: function () {
let trayButton = new St.Button ({ label: _("tray"),
style_class: 'keyboard-key' });
trayButton.key_width = 1;
trayButton.connect('button-press-event', Lang.bind(this, function () {
Main.messageTray.toggle();
}));
_onCapturedEvent: function(actor, event) {
let type = event.type();
let press = type == Clutter.EventType.BUTTON_PRESS;
let release = type == Clutter.EventType.BUTTON_RELEASE;
Main.overview.connect('showing', Lang.bind(this, function () {
trayButton.reactive = false;
trayButton.add_style_pseudo_class('grayed');
}));
Main.overview.connect('hiding', Lang.bind(this, function () {
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');
}));
if (press)
this._capturedPress = true;
else if (release && this._capturedPress)
this._hideSubkeys();
return trayButton;
return true;
},
_addRows : function (keys, layout) {
let keyboard_row = new St.BoxLayout();
for (let i = 0; i < keys.length; ++i) {
let children = keys[i].get_children();
let right_box = new St.BoxLayout({ style_class: 'keyboard-row' });
let left_box = new St.BoxLayout({ style_class: 'keyboard-row' });
let center_box = new St.BoxLayout({ style_class: 'keyboard-row' });
let right_box = new St.BoxLayout({ style_class: 'keyboard-row' });
for (let j = 0; j < children.length; ++j) {
if (this._numOfHorizKeys == 0)
this._numOfHorizKeys = children.length;
let key = children[j];
let button = new Key(key);
if (key.align == 'right')
switch (key.align) {
case 'right':
right_box.add(button.actor);
else
break;
case 'center':
center_box.add(button.actor);
break;
case 'left':
default:
left_box.add(button.actor);
break;
}
if (key.name == 'Caribou_Prefs') {
key.connect('key-released', Lang.bind(this, this.hide));
// Add new key for hiding message tray
right_box.add(this._getTrayIcon());
}
button.connect('show-subkeys', Lang.bind(this, function() {
if (this._subkeysBoxPointer)
this._subkeysBoxPointer.hide(BoxPointer.PopupAnimation.FULL);
this._subkeysBoxPointer = button.subkeys;
this._subkeysBoxPointer.show(BoxPointer.PopupAnimation.FULL);
if (!this._capturedEventId)
this._capturedEventId = this.actor.connect('captured-event',
Lang.bind(this, this._onCapturedEvent));
}));
button.connect('hide-subkeys', Lang.bind(this, function() {
this._hideSubkeys();
}));
}
keyboard_row.add(left_box, { expand: true, x_fill: false, x_align: St.Align.START });
keyboard_row.add(center_box, { expand: true, x_fill: false, x_align: St.Align.MIDDLE });
keyboard_row.add(right_box, { expand: true, x_fill: false, x_align: St.Align.END });
}
layout.add(keyboard_row);
@ -404,7 +359,7 @@ const Keyboard = new Lang.Class({
if (!this._enableKeyboard)
return;
let monitor = Main.layoutManager.bottomMonitor;
let monitor = Main.layoutManager.keyboardMonitor;
let maxHeight = monitor.height / 3;
this.actor.width = monitor.width;
@ -456,6 +411,14 @@ const Keyboard = new Lang.Class({
this._redraw();
},
_onGroupAdded: function (keyboard, gname) {
this._groups[gname] = this._createLayersForGroup(gname);
},
_onGroupRemoved: function (keyboard, gname) {
delete this._groups[gname];
},
_setActiveLayer: function () {
let active_group_name = this._keyboard.active_group;
let active_group = this._keyboard.get_group(active_group_name);
@ -491,18 +454,79 @@ const Keyboard = new Lang.Class({
actor._extended_keys || actor.extended_key;
},
show: function () {
this._redraw();
_clearKeyboardRestTimer: function() {
if (!this._keyboardRestingId)
return;
GLib.source_remove(this._keyboardRestingId);
this._keyboardRestingId = 0;
},
show: function (monitor) {
this._keyboardRequested = true;
if (this._keyboardVisible) {
if (monitor != Main.layoutManager.keyboardIndex) {
Main.layoutManager.keyboardIndex = monitor;
this._redraw();
}
return;
}
this._clearKeyboardRestTimer();
this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
KEYBOARD_REST_TIME,
Lang.bind(this, function() {
this._clearKeyboardRestTimer();
this._show(monitor);
}));
},
_show: function(monitor) {
if (!this._keyboardRequested)
return;
Main.layoutManager.keyboardIndex = monitor;
this._redraw();
Main.layoutManager.showKeyboard();
this._destroySource();
},
hide: function () {
this._keyboardRequested = false;
if (!this._keyboardVisible)
return;
this._clearKeyboardRestTimer();
this._keyboardRestingId = GLib.timeout_add(GLib.PRIORITY_DEFAULT,
KEYBOARD_REST_TIME,
Lang.bind(this, function() {
this._clearKeyboardRestTimer();
this._hide();
}));
},
_hide: function() {
if (this._keyboardRequested)
return;
this._hideSubkeys();
Main.layoutManager.hideKeyboard();
this._createSource();
},
_hideSubkeys: function() {
if (this._subkeysBoxPointer) {
this._subkeysBoxPointer.hide(BoxPointer.PopupAnimation.FULL);
this._subkeysBoxPointer = null;
}
if (this._capturedEventId) {
this.actor.disconnect(this._capturedEventId);
this._capturedEventId = 0;
}
this._capturedPress = false;
},
_moveTemporarily: function () {
let currentWindow = global.screen.get_display().focus_window;
let rect = currentWindow.get_outer_rect();
@ -531,6 +555,13 @@ const Keyboard = new Lang.Class({
return one - two;
},
_clearShowIdle: function() {
if (!this._showIdleId)
return;
GLib.source_remove(this._showIdleId);
this._showIdleId = 0;
},
// D-Bus methods
Show: function(timestamp) {
if (!this._enableKeyboard)
@ -539,9 +570,11 @@ const Keyboard = new Lang.Class({
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
return;
this._clearShowIdle();
if (timestamp != Clutter.CURRENT_TIME)
this._timestamp = timestamp;
this.show();
this.show(Main.layoutManager.focusIndex);
},
Hide: function(timestamp) {
@ -551,6 +584,8 @@ const Keyboard = new Lang.Class({
if (this._compareTimestamp(timestamp, this._timestamp) < 0)
return;
this._clearShowIdle();
if (timestamp != Clutter.CURRENT_TIME)
this._timestamp = timestamp;
this.hide();
@ -585,16 +620,13 @@ const KeyboardSource = new Lang.Class({
this.keepTrayOnSummaryClick = true;
},
handleSummaryClick: function() {
let event = Clutter.get_current_event();
if (event.type() != Clutter.EventType.BUTTON_RELEASE)
return false;
handleSummaryClick: function(button) {
this.open();
return true;
},
open: function() {
this._keyboard.show();
// Show the OSK below the message tray
this._keyboard.show(Main.layoutManager.bottomIndex);
}
});

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -39,6 +39,7 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
* in the shell core code too. */
'const stage = global.stage; ' +
/* Special lookingGlass functions */
'const inspect = Lang.bind(Main.lookingGlass, Main.lookingGlass.inspect); ' +
'const it = Main.lookingGlass.getIt(); ' +
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
@ -684,7 +685,8 @@ const Memory = new Lang.Class({
const Extensions = new Lang.Class({
Name: 'Extensions',
_init: function() {
_init: function(lookingGlass) {
this._lookingGlass = lookingGlass;
this.actor = new St.BoxLayout({ vertical: true,
name: 'lookingGlassExtensions' });
this._noExtensions = new St.Label({ style_class: 'lg-extensions-none',
@ -871,8 +873,7 @@ const LookingGlass = new Lang.Class({
inspectIcon.connect('button-press-event', Lang.bind(this, function () {
let inspector = new Inspector(this);
inspector.connect('target', Lang.bind(this, function(i, target, stageX, stageY) {
this._pushResult('<inspect x:' + stageX + ' y:' + stageY + '>',
target);
this._pushResult('inspect(' + Math.round(stageX) + ', ' + Math.round(stageY) + ')', target);
}));
inspector.connect('closed', Lang.bind(this, function() {
this.actor.show();
@ -912,7 +913,7 @@ const LookingGlass = new Lang.Class({
this._memory = new Memory();
notebook.appendPage('Memory', this._memory.actor);
this._extensions = new Extensions();
this._extensions = new Extensions(this);
notebook.appendPage('Extensions', this._extensions.actor);
this._entry.clutter_text.connect('activate', Lang.bind(this, function (o, e) {
@ -950,9 +951,7 @@ const LookingGlass = new Lang.Class({
_updateFont: function() {
let fontName = this._interfaceSettings.get_string('monospace-font-name');
// This is mishandled by the scanner - should by Pango.FontDescription_from_string(fontName);
// https://bugzilla.gnome.org/show_bug.cgi?id=595889
let fontDesc = Pango.font_description_from_string(fontName);
let fontDesc = Pango.FontDescription.from_string(fontName);
// We ignore everything but size and style; you'd be crazy to set your system-wide
// monospace font to be bold/oblique/etc. Could easily be added here.
this.actor.style =
@ -1057,6 +1056,10 @@ const LookingGlass = new Lang.Class({
this._entry.text = '';
},
inspect: function(x, y) {
return global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
},
getIt: function () {
return this._it;
},
@ -1129,7 +1132,7 @@ const LookingGlass = new Lang.Class({
if (this._open)
return;
if (!Main.pushModal(this._entry))
if (!Main.pushModal(this._entry, { keybindingMode: Shell.KeyBindingMode.LOOKING_GLASS }))
return;
this._notebook.selectIndex(0);

View File

@ -18,8 +18,10 @@ const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionDownloader = imports.ui.extensionDownloader;
const Keyboard = imports.ui.keyboard;
const MessageTray = imports.ui.messageTray;
const OsdWindow = imports.ui.osdWindow;
const Overview = imports.ui.overview;
const Panel = imports.ui.panel;
const Params = imports.misc.params;
const RunDialog = imports.ui.runDialog;
const Layout = imports.ui.layout;
const LookingGlass = imports.ui.lookingGlass;
@ -50,11 +52,13 @@ let screenShield = null;
let notificationDaemon = null;
let windowAttentionHandler = null;
let ctrlAltTabManager = null;
let osdWindow = null;
let sessionMode = null;
let shellDBusService = null;
let shellMountOpDBusService = null;
let screenSaverDBus = null;
let modalCount = 0;
let keybindingMode = Shell.KeyBindingMode.NORMAL;
let modalActorFocusStack = [];
let uiGroup = null;
let magnifier = null;
@ -66,10 +70,18 @@ let _defaultCssStylesheet = null;
let _cssStylesheet = null;
let _overridesSettings = null;
let background = null;
function _sessionUpdated() {
Meta.keybindings_set_custom_handler('panel-run-dialog', sessionMode.hasRunDialog ? openRunDialog : null);
wm.setCustomKeybindingHandler('panel-main-menu',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
sessionMode.hasOverview ? Lang.bind(overview, overview.toggle) : null);
wm.allowKeybinding('overlay-key', Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW);
wm.setCustomKeybindingHandler('panel-run-dialog',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
sessionMode.hasRunDialog ? openRunDialog : null);
if (sessionMode.isGreeter)
screenShield.showDialog();
}
@ -79,12 +91,26 @@ function start() {
global.logError = window.log;
global.log = window.log;
// Hide the stage until we're ready for it
global.stage.hide();
// Chain up async errors reported from C
global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); });
Gio.DesktopAppInfo.set_desktop_env('GNOME');
sessionMode = new SessionMode.SessionMode();
// start session after we know what mode we're running in
let signalId = sessionMode.connect('updated', function() {
sessionMode.disconnect(signalId);
startSession();
});
}
function startSession() {
sessionMode.connect('updated', _loadDefaultStylesheet);
shellDBusService = new ShellDBus.GnomeShell();
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
@ -101,48 +127,30 @@ function start() {
tracker.connect('startup-sequence-changed', _queueCheckWorkspaces);
// The stage is always covered so Clutter doesn't need to clear it; however
// the color is used as the default contents for the Mutter root background
// actor so set it anyways.
global.stage.color = DEFAULT_BACKGROUND_COLOR;
global.stage.no_clear_hint = true;
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
loadTheme();
// Set up stage hierarchy to group all UI actors under one container.
uiGroup = new Shell.GenericContainer({ name: 'uiGroup' });
uiGroup.connect('allocate',
function (actor, box, flags) {
let children = uiGroup.get_children();
for (let i = 0; i < children.length; i++)
children[i].allocate_preferred_size(flags);
});
uiGroup.connect('get-preferred-width',
function(actor, forHeight, alloc) {
let width = global.stage.width;
[alloc.min_size, alloc.natural_size] = [width, width];
});
uiGroup.connect('get-preferred-height',
function(actor, forWidth, alloc) {
let height = global.stage.height;
[alloc.min_size, alloc.natural_size] = [height, height];
});
global.window_group.reparent(uiGroup);
global.overlay_group.reparent(uiGroup);
global.stage.add_actor(uiGroup);
_loadDefaultStylesheet();
// Setup the stage hierarchy early
layoutManager = new Layout.LayoutManager();
// Various parts of the codebase still refers to Main.uiGroup
// instead using the layoutManager. This keeps that code
// working until it's updated.
uiGroup = layoutManager.uiGroup;
xdndHandler = new XdndHandler.XdndHandler();
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
osdWindow = new OsdWindow.OsdWindow();
overview = new Overview.Overview();
wm = new WindowManager.WindowManager();
magnifier = new Magnifier.Magnifier();
if (UnlockDialog.isSupported())
screenShield = new ScreenShield.ScreenShield();
else
screenShield = new ScreenShield.ScreenShieldFallback();
// The message tray relies on being constructed
// after the panel.
panel = new Panel.Panel();
wm = new WindowManager.WindowManager();
messageTray = new MessageTray.MessageTray();
keyboard = new Keyboard.Keyboard();
notificationDaemon = new NotificationDaemon.NotificationDaemon();
@ -150,24 +158,23 @@ function start() {
componentManager = new Components.ComponentManager();
layoutManager.init();
keyboard.init();
layoutManager.prepareStartupAnimation();
overview.init();
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
false, -1, 1);
Meta.keybindings_set_custom_handler('panel-main-menu', Lang.bind(overview, overview.toggle));
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
sessionMode.connect('update', _sessionUpdated);
sessionMode.connect('updated', _sessionUpdated);
_sessionUpdated();
// Provide the bus object for gnome-session to
// initiate logouts.
EndSessionDialog.init();
_startDate = new Date();
// We're ready for the session manager to move to the next phase
Meta.register_with_session();
global.stage.connect('captured-event', _globalKeyPressHandler);
_startDate = new Date();
log('GNOME Shell started at ' + _startDate);
@ -191,6 +198,10 @@ function start() {
ExtensionDownloader.init();
ExtensionSystem.init();
layoutManager.connect('startup-prepared', function() {
layoutManager.startupAnimation();
});
}
let _workspaces = [];
@ -374,6 +385,18 @@ function _nWorkspacesChanged() {
return false;
}
function _loadDefaultStylesheet() {
if (!sessionMode.isPrimary)
return;
let stylesheet = global.datadir + '/theme/' + sessionMode.stylesheetName;
if (_defaultCssStylesheet == stylesheet)
return;
_defaultCssStylesheet = stylesheet;
loadTheme();
}
/**
* getThemeStylesheet:
*
@ -454,91 +477,6 @@ function notifyError(msg, details) {
notify(msg, details);
}
function isWindowActorDisplayedOnWorkspace(win, workspaceIndex) {
return win.get_workspace() == workspaceIndex ||
(win.get_meta_window() && win.get_meta_window().is_on_all_workspaces());
}
function getWindowActorsForWorkspace(workspaceIndex) {
return global.get_window_actors().filter(function (win) {
return isWindowActorDisplayedOnWorkspace(win, workspaceIndex);
});
}
// This function encapsulates hacks to make certain global keybindings
// work even when we are in one of our modes where global keybindings
// are disabled with a global grab. (When there is a global grab, then
// all key events will be delivered to the stage, so ::captured-event
// on the stage can be used for global keybindings.)
function _globalKeyPressHandler(actor, event) {
if (modalCount == 0)
return false;
if (event.type() != Clutter.EventType.KEY_PRESS)
return false;
if (!sessionMode.allowKeybindingsWhenModal) {
if (modalCount > (overview.visible ? 1 : 0))
return false;
}
let symbol = event.get_key_symbol();
let keyCode = event.get_key_code();
let ignoredModifiers = global.display.get_ignored_modifier_mask();
let modifierState = event.get_state() & ~ignoredModifiers;
// This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
let action = global.display.get_keybinding_action(keyCode, modifierState);
if (action == Meta.KeyBindingAction.SWITCH_PANELS) {
ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK,
modifierState);
return true;
}
switch (action) {
// left/right would effectively act as synonyms for up/down if we enabled them;
// but that could be considered confusing; we also disable them in the main view.
//
// case Meta.KeyBindingAction.WORKSPACE_LEFT:
// if (!sessionMode.hasWorkspaces)
// return false;
//
// wm.actionMoveWorkspaceLeft();
// return true;
// case Meta.KeyBindingAction.WORKSPACE_RIGHT:
// if (!sessionMode.hasWorkspaces)
// return false;
//
// wm.actionMoveWorkspaceRight();
// return true;
case Meta.KeyBindingAction.WORKSPACE_UP:
if (!sessionMode.hasWorkspaces)
return false;
wm.actionMoveWorkspace(Meta.MotionDirection.UP);
return true;
case Meta.KeyBindingAction.WORKSPACE_DOWN:
if (!sessionMode.hasWorkspaces)
return false;
wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
return true;
case Meta.KeyBindingAction.PANEL_RUN_DIALOG:
case Meta.KeyBindingAction.COMMAND_2:
if (!sessionMode.hasRunDialog)
return false;
openRunDialog();
return true;
case Meta.KeyBindingAction.PANEL_MAIN_MENU:
case Meta.KeyBindingAction.OVERLAY_KEY:
overview.hide();
return true;
}
return false;
}
function _findModal(actor) {
for (let i = 0; i < modalActorFocusStack.length; i++) {
if (modalActorFocusStack[i].actor == actor)
@ -547,14 +485,10 @@ function _findModal(actor) {
return -1;
}
function isInModalStack(actor) {
return _findModal(actor) != -1;
}
/**
* pushModal:
* @actor: #ClutterActor which will be given keyboard focus
* @timestamp: optional timestamp
* @params: optional parameters
*
* Ensure we are in a mode where all keyboard and mouse input goes to
* the stage, and focus @actor. Multiple calls to this function act in
@ -565,21 +499,27 @@ function isInModalStack(actor) {
* modal stack returns to this actor, reset the focus to the actor
* which was focused at the time pushModal() was invoked.
*
* @timestamp is optionally used to associate the call with a specific user
* initiated event. If not provided then the value of
* global.get_current_time() is assumed.
* @params may be used to provide the following parameters:
* - timestamp: used to associate the call with a specific user initiated
* event. If not provided then the value of
* global.get_current_time() is assumed.
*
* @options: optional Meta.ModalOptions flags to indicate that the
* pointer is alrady grabbed
* - options: Meta.ModalOptions flags to indicate that the pointer is
* already grabbed
*
* - keybindingMode: used to set the current Shell.KeyBindingMode to filter
* global keybindings; the default of NONE will filter
* out all keybindings
*
* Returns: true iff we successfully acquired a grab or already had one
*/
function pushModal(actor, timestamp, options) {
if (timestamp == undefined)
timestamp = global.get_current_time();
function pushModal(actor, params) {
params = Params.parse(params, { timestamp: global.get_current_time(),
options: 0,
keybindingMode: Shell.KeyBindingMode.NONE });
if (modalCount == 0) {
if (!global.begin_modal(timestamp, options ? options : 0)) {
if (!global.begin_modal(params.timestamp, params.options)) {
log('pushModal: invocation of begin_modal failed');
return false;
}
@ -594,20 +534,23 @@ function pushModal(actor, timestamp, options) {
if (index >= 0)
popModal(actor);
});
let curFocus = global.stage.get_key_focus();
let curFocusDestroyId;
if (curFocus != null) {
curFocusDestroyId = curFocus.connect('destroy', function() {
let prevFocus = global.stage.get_key_focus();
let prevFocusDestroyId;
if (prevFocus != null) {
prevFocusDestroyId = prevFocus.connect('destroy', function() {
let index = _findModal(actor);
if (index >= 0)
modalActorFocusStack[index].actor = null;
modalActorFocusStack[index].prevFocus = null;
});
}
modalActorFocusStack.push({ actor: actor,
focus: curFocus,
destroyId: actorDestroyId,
focusDestroyId: curFocusDestroyId });
prevFocus: prevFocus,
prevFocusDestroyId: prevFocusDestroyId,
keybindingMode: keybindingMode });
keybindingMode = params.keybindingMode;
global.stage.set_key_focus(actor);
return true;
}
@ -634,6 +577,7 @@ function popModal(actor, timestamp) {
global.stage.set_key_focus(null);
global.end_modal(timestamp);
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
keybindingMode = Shell.KeyBindingMode.NORMAL;
throw new Error('incorrect pop');
}
@ -644,17 +588,34 @@ function popModal(actor, timestamp) {
record.actor.disconnect(record.destroyId);
if (focusIndex == modalActorFocusStack.length - 1) {
if (record.focus)
record.focus.disconnect(record.focusDestroyId);
global.stage.set_key_focus(record.focus);
if (record.prevFocus)
record.prevFocus.disconnect(record.prevFocusDestroyId);
keybindingMode = record.keybindingMode;
global.stage.set_key_focus(record.prevFocus);
} else {
// If we have:
// global.stage.set_focus(a);
// Main.pushModal(b);
// Main.pushModal(c);
// Main.pushModal(d);
//
// then we have the stack:
// [{ prevFocus: a, actor: b },
// { prevFocus: b, actor: c },
// { prevFocus: c, actor: d }]
//
// When actor c is destroyed/popped, if we only simply remove the
// record, then the focus stack will be [a, c], rather than the correct
// [a, b]. Shift the focus stack up before removing the record to ensure
// that we get the correct result.
let t = modalActorFocusStack[modalActorFocusStack.length - 1];
if (t.focus)
t.focus.disconnect(t.focusDestroyId);
if (t.prevFocus)
t.prevFocus.disconnect(t.prevFocusDestroyId);
// Remove from the middle, shift the focus chain up
for (let i = modalActorFocusStack.length - 1; i > focusIndex; i--) {
modalActorFocusStack[i].focus = modalActorFocusStack[i - 1].focus;
modalActorFocusStack[i].focusDestroyId = modalActorFocusStack[i - 1].focusDestroyId;
modalActorFocusStack[i].prevFocus = modalActorFocusStack[i - 1].prevFocus;
modalActorFocusStack[i].prevFocusDestroyId = modalActorFocusStack[i - 1].prevFocusDestroyId;
modalActorFocusStack[i].keybindingMode = modalActorFocusStack[i - 1].keybindingMode;
}
}
modalActorFocusStack.splice(focusIndex, 1);
@ -665,6 +626,7 @@ function popModal(actor, timestamp) {
global.end_modal(timestamp);
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
Meta.enable_unredirect_for_screen(global.screen);
keybindingMode = Shell.KeyBindingMode.NORMAL;
}
function createLookingGlass() {

File diff suppressed because it is too large Load Diff

View File

@ -37,10 +37,12 @@ const ModalDialog = new Lang.Class({
params = Params.parse(params, { shellReactive: false,
styleClass: null,
parentActor: Main.uiGroup,
keybindingMode: Shell.KeyBindingMode.SYSTEM_MODAL,
shouldFadeIn: true });
this.state = State.CLOSED;
this._hasModal = false;
this._keybindingMode = params.keybindingMode;
this._shellReactive = params.shellReactive;
this._shouldFadeIn = params.shouldFadeIn;
@ -56,7 +58,9 @@ const ModalDialog = new Lang.Class({
this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy));
this._actionKeys = {};
this._pressedKey = null;
this._buttonKeys = {};
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
this._group.connect('key-release-event', Lang.bind(this, this._onKeyReleaseEvent));
this._backgroundBin = new St.Bin();
@ -78,7 +82,7 @@ const ModalDialog = new Lang.Class({
let stack = new Shell.Stack();
this._backgroundBin.child = stack;
this._eventBlocker = new Clutter.Group({ reactive: true });
this._eventBlocker = new Clutter.Actor({ reactive: true });
stack.add_actor(this._eventBlocker);
stack.add_actor(this.dialogLayout);
} else {
@ -93,10 +97,9 @@ const ModalDialog = new Lang.Class({
x_align: St.Align.MIDDLE,
y_align: St.Align.START });
this._buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
visible: false,
vertical: false });
this.dialogLayout.add(this._buttonLayout,
this.buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
vertical: false });
this.dialogLayout.add(this.buttonLayout,
{ expand: true,
x_align: St.Align.MIDDLE,
y_align: St.Align.END });
@ -111,35 +114,16 @@ const ModalDialog = new Lang.Class({
this._group.destroy();
},
setActionKey: function(key, action) {
this._actionKeys[key] = action;
clearButtons: function() {
this.buttonLayout.destroy_all_children();
this._buttonKeys = {};
},
setButtons: function(buttons) {
this._buttonLayout.destroy_all_children();
this._actionKeys = {};
this._buttonLayout.visible = (buttons.length > 0);
this.clearButtons();
for (let i = 0; i < buttons.length; i++) {
let buttonInfo = buttons[i];
let label = buttonInfo['label'];
let action = buttonInfo['action'];
let key = buttonInfo['key'];
let isDefault = buttonInfo['default'];
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,
can_focus: true,
label: label });
if (isDefault)
buttonInfo.button.add_style_pseudo_class('default');
let x_alignment;
if (buttons.length == 1)
@ -151,29 +135,72 @@ const ModalDialog = new Lang.Class({
else
x_alignment = St.Align.MIDDLE;
if (!this._initialKeyFocusDestroyId)
this._initialKeyFocus = buttonInfo.button;
this._buttonLayout.add(buttonInfo.button,
{ expand: true,
x_fill: false,
y_fill: false,
x_align: x_alignment,
y_align: St.Align.MIDDLE });
buttonInfo.button.connect('clicked', action);
if (key)
this._actionKeys[key] = action;
this.addButton(buttonInfo, { expand: true,
x_fill: false,
y_fill: false,
x_align: x_alignment,
y_align: St.Align.MIDDLE });
}
},
this.emit('buttons-set');
addButton: function(buttonInfo, layoutInfo) {
let label = buttonInfo['label'];
let action = buttonInfo['action'];
let key = buttonInfo['key'];
let isDefault = buttonInfo['default'];
let keys;
if (key)
keys = [key];
else if (isDefault)
keys = [Clutter.KEY_Return, Clutter.KEY_KP_Enter, Clutter.KEY_ISO_Enter];
else
keys = [];
let button = new St.Button({ style_class: 'modal-dialog-button',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
reactive: true,
can_focus: true,
label: label });
button.connect('clicked', action);
buttonInfo['button'] = button;
if (isDefault)
button.add_style_pseudo_class('default');
if (!this._initialKeyFocusDestroyId)
this._initialKeyFocus = button;
for (let i in keys)
this._buttonKeys[keys[i]] = buttonInfo;
this.buttonLayout.add(button, layoutInfo);
return button;
},
_onKeyPressEvent: function(object, event) {
this._pressedKey = event.get_key_symbol();
},
_onKeyReleaseEvent: function(object, event) {
let symbol = event.get_key_symbol();
let action = this._actionKeys[symbol];
let pressedKey = this._pressedKey;
this._pressedKey = null;
if (action) {
let symbol = event.get_key_symbol();
if (symbol != pressedKey)
return false;
let buttonInfo = this._buttonKeys[symbol];
if (!buttonInfo)
return false;
let button = buttonInfo['button'];
let action = buttonInfo['action'];
if (action && button.reactive) {
action();
return true;
}
@ -226,7 +253,7 @@ const ModalDialog = new Lang.Class({
if (this.state == State.OPENED || this.state == State.OPENING)
return true;
if (!this.pushModal(timestamp))
if (!this.pushModal({ timestamp: timestamp }))
return false;
this._fadeOpen(onPrimary);
@ -249,6 +276,7 @@ const ModalDialog = new Lang.Class({
function() {
this.state = State.CLOSED;
this._group.hide();
this.emit('closed');
})
});
},
@ -276,7 +304,8 @@ const ModalDialog = new Lang.Class({
pushModal: function (timestamp) {
if (this._hasModal)
return true;
if (!Main.pushModal(this._group, timestamp))
if (!Main.pushModal(this._group, { timestamp: timestamp,
keybindingMode: this._keybindingMode }))
return false;
this._hasModal = true;

View File

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

179
js/ui/osdWindow.js Normal file
View File

@ -0,0 +1,179 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const St = imports.gi.St;
const Lang = imports.lang;
const Layout = imports.ui.layout;
const Main = imports.ui.main;
const Mainloop = imports.mainloop;
const Tweener = imports.ui.tweener;
const HIDE_TIMEOUT = 1500;
const FADE_TIME = 0.1;
const LEVEL_ANIMATION_TIME = 0.1;
const LevelBar = new Lang.Class({
Name: 'LevelBar',
_init: function() {
this._level = 0;
this.actor = new St.Bin({ style_class: 'level',
x_fill: true, y_fill: true });
this._bar = new St.DrawingArea();
this._bar.connect('repaint', Lang.bind(this, this._repaint));
this.actor.set_child(this._bar);
},
get level() {
return this._level;
},
set level(value) {
let newValue = Math.max(0, Math.min(value, 100));
if (newValue == this._level)
return;
this._level = newValue;
this._bar.queue_repaint();
},
_repaint: function() {
let cr = this._bar.get_context();
let node = this.actor.get_theme_node();
let radius = node.get_border_radius(0); // assume same radius for all corners
Clutter.cairo_set_source_color(cr, node.get_foreground_color());
let [w, h] = this._bar.get_surface_size();
w *= (this._level / 100.);
if (w == 0)
return;
cr.moveTo(radius, 0);
if (w >= radius)
cr.arc(w - radius, radius, radius, 1.5 * Math.PI, 2. * Math.PI);
else
cr.lineTo(w, 0);
if (w >= radius)
cr.arc(w - radius, h - radius, radius, 0, 0.5 * Math.PI);
else
cr.lineTo(w, h);
cr.arc(radius, h - radius, radius, 0.5 * Math.PI, Math.PI);
cr.arc(radius, radius, radius, Math.PI, 1.5 * Math.PI);
cr.fill();
}
});
const OsdWindow = new Lang.Class({
Name: 'OsdWindow',
_init: function() {
this.actor = new St.Widget({ x_expand: true,
y_expand: true,
x_align: Clutter.ActorAlign.CENTER,
y_align: Clutter.ActorAlign.CENTER });
this.actor.add_constraint(new Layout.MonitorConstraint({ primary: true }));
this._box = new St.BoxLayout({ style_class: 'osd-window',
vertical: true });
this.actor.add_actor(this._box);
this._icon = new St.Icon();
this._box.add(this._icon, { expand: true });
this._label = new St.Label();
this._box.add(this._label);
this._level = new LevelBar();
this._box.add(this._level.actor);
this._hideTimeoutId = 0;
this._reset();
Main.layoutManager.connect('monitors-changed',
Lang.bind(this, this._monitorsChanged));
this._monitorsChanged();
Main.layoutManager.addChrome(this.actor, { affectsInputRegion: false });
},
setIcon: function(icon) {
this._icon.gicon = icon;
},
setLabel: function(label) {
this._label.visible = (label != undefined);
if (label)
this._label.text = label;
},
setLevel: function(level) {
this._level.actor.visible = (level != undefined);
if (this.actor.visible)
Tweener.addTween(this._level,
{ level: level,
time: LEVEL_ANIMATION_TIME,
transition: 'easeOutQuad' });
else
this._level.level = level;
},
show: function() {
if (!this._icon.gicon)
return;
if (!this.actor.visible) {
this.actor.show();
this.actor.opacity = 0;
Tweener.addTween(this.actor,
{ opacity: 255,
time: FADE_TIME,
transition: 'easeOutQuad' });
}
if (this._hideTimeoutId)
Mainloop.source_remove(this._hideTimeoutId);
this._hideTimeoutId = Mainloop.timeout_add(HIDE_TIMEOUT,
Lang.bind(this, this._hide));
},
cancel: function() {
if (!this._hideTimeoutId)
return;
Mainloop.source_remove(this._hideTimeoutId);
this._hideTimeoutId = 0;
this._hide();
},
_hide: function() {
Tweener.addTween(this.actor,
{ opacity: 0,
time: FADE_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, this._reset) });
},
_reset: function() {
this.actor.hide();
this.setLabel(null);
this.setLevel(null);
},
_monitorsChanged: function() {
/* assume 130x130 on a 640x480 display and scale from there */
let monitor = Main.layoutManager.primaryMonitor;
let scalew = monitor.width / 640.0;
let scaleh = monitor.height / 480.0;
let scale = Math.min(scalew, scaleh);
let size = 130 * Math.max(1, scale);
this._box.set_size(size, size);
this._box.translation_y = monitor.height / 4;
this._icon.icon_size = size / 2;
}
});

View File

@ -10,10 +10,13 @@ const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Gdk = imports.gi.Gdk;
const Background = imports.ui.background;
const Dash = imports.ui.dash;
const DND = imports.ui.dnd;
const LayoutManager = imports.ui.layout;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const OverviewControls = imports.ui.overviewControls;
const Panel = imports.ui.panel;
const Params = imports.misc.params;
const Tweener = imports.ui.tweener;
@ -23,23 +26,13 @@ const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
// Time for initial animation going into Overview mode
const ANIMATION_TIME = 0.25;
// XXX -- grab this automatically. Hard to do.
const DASH_MAX_WIDTH = 96;
// Must be less than ANIMATION_TIME, since we switch to
// or from the overview completely after ANIMATION_TIME,
// and don't want the shading animation to get cut off
const SHADE_ANIMATION_TIME = .20;
const DND_WINDOW_SWITCH_TIMEOUT = 1250;
const SwipeScrollDirection = {
NONE: 0,
HORIZONTAL: 1,
VERTICAL: 2
};
const SwipeScrollResult = {
CANCEL: 0,
SWIPE: 1,
CLICK: 2
};
const ShellInfo = new Lang.Class({
Name: 'ShellInfo',
@ -57,7 +50,14 @@ const ShellInfo = new Lang.Class({
this._source.destroy();
},
setMessage: function(text, undoCallback, undoLabel) {
setMessage: function(text, options) {
options = Params.parse(options, { undoCallback: null,
forFeedback: false
});
let undoCallback = options.undoCallback;
let forFeedback = options.forFeedback;
if (this._source == null) {
this._source = new MessageTray.SystemNotificationSource();
this._source.connect('destroy', Lang.bind(this,
@ -71,6 +71,7 @@ const ShellInfo = new Lang.Class({
if (this._source.notifications.length == 0) {
notification = new MessageTray.Notification(this._source, text, null);
notification.setTransient(true);
notification.setForFeedback(forFeedback);
} else {
notification = this._source.notifications[0];
notification.update(text, null, { clear: true });
@ -78,10 +79,8 @@ const ShellInfo = new Lang.Class({
this._undoCallback = undoCallback;
if (undoCallback) {
notification.addButton('system-undo',
undoLabel ? undoLabel : _("Undo"));
notification.connect('action-invoked',
Lang.bind(this, this._onUndoClicked));
notification.addButton('system-undo', _("Undo"));
notification.connect('action-invoked', Lang.bind(this, this._onUndoClicked));
}
this._source.notify(notification);
@ -93,7 +92,10 @@ const Overview = new Lang.Class({
_init: function() {
this._overviewCreated = false;
this._initCalled = false;
this._controlPressed = false;
global.stage.connect('captured-event', Lang.bind(this, this._capturedEvent));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
this._sessionUpdated();
},
@ -107,59 +109,61 @@ const Overview = new Lang.Class({
this._overviewCreated = true;
// The main BackgroundActor is inside global.window_group which is
// The main Background actors are inside global.window_group which are
// hidden when displaying the overview, so we create a new
// one. Instances of this class share a single CoglTexture behind the
// scenes which allows us to show the background with different
// rendering options without duplicating the texture data.
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
this._background.hide();
global.overlay_group.add_actor(this._background);
let monitor = Main.layoutManager.primaryMonitor;
this._desktopFade = new St.Bin();
global.overlay_group.add_actor(this._desktopFade);
this._spacing = 0;
let layout = new Clutter.BinLayout();
this._stack = new Clutter.Actor({ layout_manager: layout });
this._stack.add_constraint(new LayoutManager.MonitorConstraint({ primary: true }));
/* 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,
x_expand: true,
y_expand: true });
this._overview._delegate = this;
this._scrollDirection = SwipeScrollDirection.NONE;
this._scrollAdjustment = null;
this._capturedEventId = 0;
this._buttonPressId = 0;
this._groupStack = new St.Widget({ layout_manager: new Clutter.BinLayout(),
x_expand: true, y_expand: true,
clip_to_allocation: true });
this._group = new St.BoxLayout({ name: 'overview-group',
reactive: true,
x_expand: true, y_expand: true });
this._groupStack.add_actor(this._group);
this._backgroundGroup = new Meta.BackgroundGroup();
global.overlay_group.add_child(this._backgroundGroup);
this._backgroundGroup.hide();
this._bgManagers = [];
this.visible = false; // animating to overview, in overview, animating out
this._shown = false; // show() and not hide()
this._shownTemporarily = false; // showTemporarily() and not hideTemporarily()
this._modal = false; // have a modal grab
this.animationInProgress = false;
this._hideInProgress = false;
this.visibleTarget = 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._stack.hide();
this._stack.add_actor(this._overview);
global.overlay_group.add_actor(this._stack);
this._coverPane.hide();
@ -171,11 +175,81 @@ 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._group.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
this._windowSwitchTimeoutId = 0;
this._windowSwitchTimestamp = 0;
this._lastActiveWorkspaceIndex = -1;
this._lastHoveredWindow = null;
this._needsFakePointerEvent = false;
if (this._initCalled)
this.init();
},
_updateBackgrounds: function() {
for (let i = 0; i < this._bgManagers.length; i++)
this._bgManagers[i].destroy();
this._bgManagers = [];
for (let i = 0; i < Main.layoutManager.monitors.length; i++) {
let bgManager = new Background.BackgroundManager({ container: this._backgroundGroup,
monitorIndex: i,
effects: Meta.BackgroundEffects.VIGNETTE });
this._bgManagers.push(bgManager);
}
},
_unshadeBackgrounds: function() {
let backgrounds = this._backgroundGroup.get_children();
for (let i = 0; i < backgrounds.length; i++) {
let background = backgrounds[i]._delegate;
Tweener.addTween(background,
{ brightness: 1.0,
time: SHADE_ANIMATION_TIME,
transition: 'easeOutQuad'
});
Tweener.addTween(background,
{ vignetteSharpness: 0.0,
time: SHADE_ANIMATION_TIME,
transition: 'easeOutQuad'
});
}
},
_shadeBackgrounds: function() {
let backgrounds = this._backgroundGroup.get_children();
for (let i = 0; i < backgrounds.length; i++) {
let background = backgrounds[i]._delegate;
Tweener.addTween(background,
{ brightness: 0.8,
time: SHADE_ANIMATION_TIME,
transition: 'easeOutQuad'
});
Tweener.addTween(background,
{ vignetteSharpness: 0.7,
time: SHADE_ANIMATION_TIME,
transition: 'easeOutQuad'
});
}
},
_capturedEvent: function(actor, event) {
let type = event.type();
if (type != Clutter.EventType.KEY_PRESS &&
type != Clutter.EventType.KEY_RELEASE)
return false;
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Control_L ||
symbol == Clutter.KEY_Control_R)
this._controlPressed = type == Clutter.EventType.KEY_PRESS;
return false;
},
_sessionUpdated: function() {
@ -188,39 +262,65 @@ const Overview = new Lang.Class({
// signal handlers and so forth. So we create them after
// construction in this init() method.
init: function() {
this._initCalled = true;
if (this.isDummy)
return;
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
active; it should not exceed ~30
characters. */
hint_text: _("Type to search..."),
hint_text: _("Type to search"),
track_hover: true,
can_focus: true });
this._group.add_actor(this._searchEntry);
this._searchEntryBin = new St.Bin({ child: this._searchEntry,
x_align: St.Align.MIDDLE });
this._overview.add_actor(this._searchEntryBin);
// Create controls
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._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
this._controls = new OverviewControls.ControlsManager(this._dash,
this._thumbnailsBox,
this._viewSelector);
this._controls.dashActor.x_align = Clutter.ActorAlign.START;
this._controls.dashActor.y_expand = true;
// Put the dash in a separate layer to allow content to be centered
this._groupStack.add_actor(this._controls.dashActor);
// Pack all the actors into the group
this._group.add_actor(this._controls.dashSpacer);
this._group.add(this._viewSelector.actor, { x_fill: true,
expand: true });
this._group.add_actor(this._controls.thumbnailsActor);
// Add our same-line elements after the search entry
this._overview.add(this._groupStack, { y_fill: true, expand: true });
this._stack.add_actor(this._controls.indicatorActor);
// TODO - recalculate everything when desktop size changes
this._dash.actor.add_constraint(this._viewSelector.constrainHeight);
this.dashIconSize = this._dash.iconSize;
this._dash.connect('icon-size-changed',
Lang.bind(this, function() {
this.dashIconSize = this._dash.iconSize;
}));
// Translators: this is the name of the dock/favorites area on
// the left of the overview
Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks-symbolic');
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
this._relayout();
},
@ -233,11 +333,16 @@ const Overview = new Lang.Class({
this._viewSelector.removeSearchProvider(provider);
},
setMessage: function(text, undoCallback, undoLabel) {
//
// options:
// - undoCallback (function): the callback to be called if undo support is needed
// - forFeedback (boolean): whether the message is for direct feedback of a user action
//
setMessage: function(text, options) {
if (this.isDummy)
return;
this._shellInfo.setMessage(text, undoCallback, undoLabel);
this._shellInfo.setMessage(text, options);
},
_onDragBegin: function() {
@ -308,159 +413,15 @@ const Overview = new Lang.Class({
return DND.DragMotionResult.CONTINUE;
},
setScrollAdjustment: function(adjustment, direction) {
_onScrollEvent: function(actor, event) {
this.emit('scroll-event', event);
},
addAction: function(action) {
if (this.isDummy)
return;
this._scrollAdjustment = adjustment;
if (this._scrollAdjustment == null)
this._scrollDirection = SwipeScrollDirection.NONE;
else
this._scrollDirection = direction;
},
_onButtonPress: function(actor, event) {
if (this._scrollDirection == SwipeScrollDirection.NONE
|| event.get_button() != 1)
return;
let [stageX, stageY] = event.get_coords();
this._dragStartX = this._dragX = stageX;
this._dragStartY = this._dragY = stageY;
this._dragStartValue = this._scrollAdjustment.value;
this._lastMotionTime = -1; // used to track "stopping" while swipe-scrolling
this._capturedEventId = global.stage.connect('captured-event',
Lang.bind(this, this._onCapturedEvent));
this.emit('swipe-scroll-begin');
},
_onCapturedEvent: function(actor, event) {
let stageX, stageY;
let threshold = Gtk.Settings.get_default().gtk_dnd_drag_threshold;
switch(event.type()) {
case Clutter.EventType.BUTTON_RELEASE:
[stageX, stageY] = event.get_coords();
// default to snapping back to the original value
let newValue = this._dragStartValue;
let minValue = this._scrollAdjustment.lower;
let maxValue = this._scrollAdjustment.upper - this._scrollAdjustment.page_size;
let direction;
if (this._scrollDirection == SwipeScrollDirection.HORIZONTAL) {
direction = stageX > this._dragStartX ? -1 : 1;
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
direction *= -1;
} else {
direction = stageY > this._dragStartY ? -1 : 1;
}
// We default to scroll a full page size; both the first
// and the last page may be smaller though, so we need to
// adjust difference in those cases.
let difference = direction * this._scrollAdjustment.page_size;
if (this._dragStartValue + difference > maxValue)
difference = maxValue - this._dragStartValue;
else if (this._dragStartValue + difference < minValue)
difference = minValue - this._dragStartValue;
// If the user has moved more than half the scroll
// difference, we want to "settle" to the new value
// even if the user stops dragging rather "throws" by
// releasing during the drag.
let distance = this._dragStartValue - this._scrollAdjustment.value;
let noStop = Math.abs(distance / difference) > 0.5;
// We detect if the user is stopped by comparing the
// timestamp of the button release with the timestamp of
// the last motion. Experimentally, a difference of 0 or 1
// millisecond indicates that the mouse is in motion, a
// larger difference indicates that the mouse is stopped.
if ((this._lastMotionTime > 0 &&
this._lastMotionTime > event.get_time() - 2) ||
noStop) {
if (this._dragStartValue + difference >= minValue &&
this._dragStartValue + difference <= maxValue)
newValue += difference;
}
let result;
// See if the user has moved the mouse enough to trigger
// a drag
if (Math.abs(stageX - this._dragStartX) < threshold &&
Math.abs(stageY - this._dragStartY) < threshold) {
// no motion? It's a click!
result = SwipeScrollResult.CLICK;
this.emit('swipe-scroll-end', result);
} else {
if (newValue == this._dragStartValue)
result = SwipeScrollResult.CANCEL;
else
result = SwipeScrollResult.SWIPE;
// The event capture handler is disconnected
// while scrolling to the final position, so
// to avoid undesired prelights we raise
// the cover pane.
this._coverPane.raise_top();
this._coverPane.show();
Tweener.addTween(this._scrollAdjustment,
{ value: newValue,
time: ANIMATION_TIME,
transition: 'easeOutQuad',
onCompleteScope: this,
onComplete: function() {
this._coverPane.hide();
this.emit('swipe-scroll-end',
result);
}
});
}
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
return result != SwipeScrollResult.CLICK;
case Clutter.EventType.MOTION:
[stageX, stageY] = event.get_coords();
let dx = this._dragX - stageX;
let dy = this._dragY - stageY;
let primary = Main.layoutManager.primaryMonitor;
this._dragX = stageX;
this._dragY = stageY;
this._lastMotionTime = event.get_time();
// See if the user has moved the mouse enough to trigger
// a drag
if (Math.abs(stageX - this._dragStartX) < threshold &&
Math.abs(stageY - this._dragStartY) < threshold)
return true;
if (this._scrollDirection == SwipeScrollDirection.HORIZONTAL) {
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
this._scrollAdjustment.value -= (dx / primary.width) * this._scrollAdjustment.page_size;
else
this._scrollAdjustment.value += (dx / primary.width) * this._scrollAdjustment.page_size;
} else {
this._scrollAdjustment.value += (dy / primary.height) * this._scrollAdjustment.page_size;
}
return true;
// Block enter/leave events to avoid prelights
// during swipe-scroll
case Clutter.EventType.ENTER:
case Clutter.EventType.LEAVE:
return true;
}
return false;
this._overview.add_action(action);
},
_getDesktopClone: function() {
@ -485,42 +446,24 @@ const Overview = new Lang.Class({
// when it is next shown.
this.hide();
let primary = Main.layoutManager.primaryMonitor;
let rtl = (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL);
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
let contentY = Main.panel.actor.height;
let contentHeight = primary.height - contentY - Main.messageTray.actor.height;
this._coverPane.set_position(0, workArea.y);
this._coverPane.set_size(workArea.width, workArea.height);
this._group.set_position(primary.x, primary.y);
this._group.set_size(primary.width, primary.height);
this._updateBackgrounds();
},
this._coverPane.set_position(0, contentY);
this._coverPane.set_size(primary.width, contentHeight);
_onRestacked: function() {
let stack = global.get_window_actors();
let stackIndices = {};
let searchWidth = this._searchEntry.get_width();
let searchHeight = this._searchEntry.get_height();
let searchX = (primary.width - searchWidth) / 2;
let searchY = contentY + this._spacing;
let dashWidth = DASH_MAX_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('windows-restacked', stackIndices);
},
//// Public methods ////
@ -557,15 +500,11 @@ const Overview = new Lang.Class({
return;
if (this._shown)
return;
// Do this manually instead of using _syncInputMode, to handle failure
if (!Main.pushModal(this._group))
return;
this._modal = true;
this._animateVisible();
this._shown = true;
this._buttonPressId = this._group.connect('button-press-event',
Lang.bind(this, this._onButtonPress));
this._syncInputMode();
if (!this._modal)
return;
this._animateVisible();
},
fadeInDesktop: function() {
@ -596,6 +535,7 @@ const Overview = new Lang.Class({
this.visible = true;
this.animationInProgress = true;
this.visibleTarget = true;
// All the the actors in the window group are completely obscured,
// hiding the group holding them while the Overview is displayed greatly
@ -608,24 +548,20 @@ 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._background.show();
global.top_window_group.hide();
this._stack.show();
this._backgroundGroup.show();
this._viewSelector.show();
this._group.opacity = 0;
Tweener.addTween(this._group,
this._stack.opacity = 0;
Tweener.addTween(this._stack,
{ opacity: 255,
transition: 'easeOutQuad',
time: ANIMATION_TIME,
onComplete: this._showDone,
onCompleteScope: this
});
Tweener.addTween(this._background,
{ dim_factor: 0.4,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
this._shadeBackgrounds();
this._coverPane.raise_top();
this._coverPane.show();
@ -660,15 +596,14 @@ const Overview = new Lang.Class({
if (!this._shown)
return;
if (this._controlPressed)
return;
if (!this._shownTemporarily)
this._animateNotVisible();
this._shown = false;
this._syncInputMode();
if (this._buttonPressId > 0)
this._group.disconnect(this._buttonPressId);
this._buttonPressId = 0;
},
// hideTemporarily:
@ -692,7 +627,7 @@ const Overview = new Lang.Class({
if (this.isDummy)
return;
if (this._shown)
if (this.visible)
this.hide();
else
this.show();
@ -709,20 +644,21 @@ const Overview = new Lang.Class({
if (this._shown) {
if (!this._modal) {
if (Main.pushModal(this._group))
if (Main.pushModal(this._overview,
{ keybindingMode: Shell.KeyBindingMode.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)
@ -735,24 +671,19 @@ const Overview = new Lang.Class({
return;
this.animationInProgress = true;
this._hideInProgress = true;
this.visibleTarget = false;
this._viewSelector.zoomFromOverview();
// Make other elements fade out.
Tweener.addTween(this._group,
Tweener.addTween(this._stack,
{ opacity: 0,
transition: 'easeOutQuad',
time: ANIMATION_TIME,
onComplete: this._hideDone,
onCompleteScope: this
});
Tweener.addTween(this._background,
{ dim_factor: 1.0,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
this._unshadeBackgrounds();
this._coverPane.raise_top();
this._coverPane.show();
@ -778,15 +709,15 @@ const Overview = new Lang.Class({
Meta.enable_unredirect_for_screen(global.screen);
global.window_group.show();
global.top_window_group.show();
this._viewSelector.hide();
this._desktopFade.hide();
this._background.hide();
this._group.hide();
this._backgroundGroup.hide();
this._stack.hide();
this.visible = false;
this.animationInProgress = false;
this._hideInProgress = false;
this._coverPane.hide();

551
js/ui/overviewControls.js Normal file
View File

@ -0,0 +1,551 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Main = imports.ui.main;
const Params = imports.misc.params;
const Tweener = imports.ui.tweener;
const ViewSelector = imports.ui.viewSelector;
const SIDE_CONTROLS_ANIMATION_TIME = 0.16;
function getRtlSlideDirection(direction, actor) {
let rtl = (actor.text_direction == Clutter.TextDirection.RTL);
if (rtl)
direction = (direction == SlideDirection.LEFT) ?
SlideDirection.RIGHT : SlideDirection.LEFT;
return direction;
};
const SlideDirection = {
LEFT: 0,
RIGHT: 1
};
const SlideLayout = new Lang.Class({
Name: 'SlideLayout',
Extends: Clutter.FixedLayout,
_init: function(params) {
this._slideX = 1;
this._direction = SlideDirection.LEFT;
this.parent(params);
},
vfunc_get_preferred_width: function(container, forHeight) {
let child = container.get_first_child();
let [minWidth, natWidth] = child.get_preferred_width(forHeight);
minWidth *= this._slideX;
natWidth *= this._slideX;
return [minWidth, natWidth];
},
vfunc_allocate: function(container, box, flags) {
let child = container.get_first_child();
let [, , natWidth, natHeight] = child.get_preferred_size();
let availWidth = Math.round(box.x2 - box.x1);
let availHeight = Math.round(box.y2 - box.y1);
let realDirection = getRtlSlideDirection(this._direction, child);
let translationX = (realDirection == SlideDirection.LEFT) ?
(availWidth - natWidth) : (natWidth - availWidth);
let actorBox = new Clutter.ActorBox({ x1: translationX,
y1: 0,
x2: child.x_expand ? availWidth : natWidth,
y2: child.y_expand ? availHeight : natHeight });
child.allocate(actorBox, flags);
},
set slideX(value) {
this._slideX = value;
this.layout_changed();
},
get slideX() {
return this._slideX;
},
set slideDirection(direction) {
this._direction = direction;
this.layout_changed();
},
get slideDirection() {
return this._direction;
}
});
const SlidingControl = new Lang.Class({
Name: 'SlidingControl',
_init: function(params) {
params = Params.parse(params, { slideDirection: SlideDirection.LEFT });
this.visible = true;
this.inDrag = false;
this.layout = new SlideLayout();
this.layout.slideDirection = params.slideDirection;
this.actor = new St.Widget({ layout_manager: this.layout,
style_class: 'overview-controls',
clip_to_allocation: true });
Main.overview.connect('showing', Lang.bind(this, this._onOverviewShowing));
Main.overview.connect('item-drag-begin', Lang.bind(this, this._onDragBegin));
Main.overview.connect('item-drag-end', Lang.bind(this, this._onDragEnd));
Main.overview.connect('item-drag-cancelled', Lang.bind(this, this._onDragEnd));
Main.overview.connect('window-drag-begin', Lang.bind(this, this._onWindowDragBegin));
Main.overview.connect('window-drag-cancelled', Lang.bind(this, this._onWindowDragEnd));
Main.overview.connect('window-drag-end', Lang.bind(this, this._onWindowDragEnd));
},
getSlide: function() {
throw new Error('getSlide() must be overridden');
},
updateSlide: function() {
Tweener.addTween(this.layout, { slideX: this.getSlide(),
time: SIDE_CONTROLS_ANIMATION_TIME,
transition: 'easeOutQuad' });
},
getVisibleWidth: function() {
let child = this.actor.get_first_child();
let [, , natWidth, ] = child.get_preferred_size();
return natWidth;
},
_getTranslation: function() {
let child = this.actor.get_first_child();
let direction = getRtlSlideDirection(this.layout.slideDirection, child);
let visibleWidth = this.getVisibleWidth();
if (direction == SlideDirection.LEFT)
return - visibleWidth;
else
return visibleWidth;
},
_updateTranslation: function() {
let translationStart = 0;
let translationEnd = 0;
let translation = this._getTranslation();
if (this.visible) {
translationStart = translation;
} else {
translationEnd = translation;
}
if (this.actor.translation_x == translationEnd)
return;
this.actor.translation_x = translationStart;
Tweener.addTween(this.actor, { translation_x: translationEnd,
time: SIDE_CONTROLS_ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
_onOverviewShowing: function() {
// reset any translation and make sure the actor is visible when
// entering the overview
this.visible = true;
this.layout.slideX = this.getSlide();
this.actor.translation_x = 0;
},
_onWindowDragBegin: function() {
this._onDragBegin();
},
_onWindowDragEnd: function() {
this._onDragEnd();
},
_onDragBegin: function() {
this.inDrag = true;
this.actor.translation_x = 0;
this.updateSlide();
},
_onDragEnd: function() {
this.inDrag = false;
this.updateSlide();
},
fadeIn: function() {
Tweener.addTween(this.actor, { opacity: 255,
time: SIDE_CONTROLS_ANIMATION_TIME / 2,
transition: 'easeInQuad'
});
},
fadeHalf: function() {
Tweener.addTween(this.actor, { opacity: 128,
time: SIDE_CONTROLS_ANIMATION_TIME / 2,
transition: 'easeOutQuad'
});
},
slideIn: function() {
this.visible = true;
// we will update slideX and the translation from pageEmpty
},
slideOut: function() {
this.visible = false;
this._updateTranslation();
// we will update slideX from pageEmpty
},
pageEmpty: function() {
// When pageEmpty is received, there's no visible view in the
// selector; this means we can now safely set the full slide for
// the next page, since slideIn or slideOut might have been called,
// changing the visiblity
this.layout.slideX = this.getSlide();
this._updateTranslation();
}
});
const ThumbnailsSlider = new Lang.Class({
Name: 'ThumbnailsSlider',
Extends: SlidingControl,
_init: function(thumbnailsBox) {
this.parent({ slideDirection: SlideDirection.RIGHT });
this._thumbnailsBox = thumbnailsBox;
// SlideLayout reads the actor's expand flags to decide
// whether to allocate the natural size to its child, or the whole
// available allocation
this._thumbnailsBox.actor.y_expand = true;
this.actor.request_mode = Clutter.RequestMode.WIDTH_FOR_HEIGHT;
this.actor.reactive = true;
this.actor.track_hover = true;
this.actor.add_actor(this._thumbnailsBox.actor);
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this.updateSlide));
this.actor.connect('notify::hover', Lang.bind(this, this.updateSlide));
},
_getAlwaysZoomOut: function() {
// Always show the pager when hover, during a drag, or if workspaces are
// actually used, e.g. there are windows on more than one
let alwaysZoomOut = this.actor.hover || this.inDrag || !Meta.prefs_get_dynamic_workspaces() || global.screen.n_workspaces > 2;
if (!alwaysZoomOut) {
let monitors = Main.layoutManager.monitors;
let primary = Main.layoutManager.primaryMonitor;
/* Look for any monitor to the right of the primary, if there is
* one, we always keep zoom out, otherwise its hard to reach
* the thumbnail area without passing into the next monitor. */
for (let i = 0; i < monitors.length; i++) {
if (monitors[i].x >= primary.x + primary.width) {
alwaysZoomOut = true;
break;
}
}
}
return alwaysZoomOut;
},
getSlide: function() {
if (!this.visible)
return 0;
let alwaysZoomOut = this._getAlwaysZoomOut();
if (alwaysZoomOut)
return 1;
let child = this.actor.get_first_child();
let preferredHeight = child.get_preferred_height(-1)[1];
let expandedWidth = child.get_preferred_width(preferredHeight)[1];
let visibleWidth = child.get_theme_node().get_length('visible-width');
return visibleWidth / expandedWidth;
},
getVisibleWidth: function() {
let alwaysZoomOut = this._getAlwaysZoomOut();
if (alwaysZoomOut)
return this.parent();
let child = this.actor.get_first_child();
return child.get_theme_node().get_length('visible-width');
}
});
const DashSlider = new Lang.Class({
Name: 'DashSlider',
Extends: SlidingControl,
_init: function(dash) {
this.parent({ slideDirection: SlideDirection.LEFT });
this._dash = dash;
// SlideLayout reads the actor's expand flags to decide
// whether to allocate the natural size to its child, or the whole
// available allocation
this._dash.actor.x_expand = true;
this._dash.actor.y_expand = true;
this.actor.add_actor(this._dash.actor);
this._dash.connect('icon-size-changed', Lang.bind(this, this.updateSlide));
},
getSlide: function() {
if (this.visible || this.inDrag)
return 1;
else
return 0;
},
_onWindowDragBegin: function() {
this.fadeHalf();
},
_onWindowDragEnd: function() {
this.fadeIn();
}
});
const DashSpacer = new Lang.Class({
Name: 'DashSpacer',
Extends: St.Widget,
_init: function(params) {
this.parent(params);
this._bindConstraint = null;
},
setDashActor: function(dashActor) {
if (this._bindConstraint) {
this.remove_constraint(this._bindConstraint);
this._bindConstraint = null;
}
if (dashActor) {
this._bindConstraint = new Clutter.BindConstraint({ source: dashActor,
coordinate: Clutter.BindCoordinate.SIZE });
this.add_constraint(this._bindConstraint);
}
},
vfunc_get_preferred_width: function(forHeight) {
let box = this.get_allocation_box();
let minWidth = this.parent(forHeight)[0];
let natWidth = box.x2 - box.x1;
return [minWidth, natWidth];
},
vfunc_get_preferred_height: function(forWidth) {
let box = this.get_allocation_box();
let minHeight = this.parent(forWidth)[0];
let natHeight = box.y2 - box.y1;
return [minHeight, natHeight];
}
});
const MessagesIndicator = new Lang.Class({
Name: 'MessagesIndicator',
_init: function(viewSelector) {
this._count = 0;
this._sources = [];
this._viewSelector = viewSelector;
this._container = new St.BoxLayout({ style_class: 'messages-indicator-contents',
reactive: true,
track_hover: true,
x_expand: true,
y_expand: true,
x_align: Clutter.ActorAlign.CENTER });
this._icon = new St.Icon({ icon_name: 'user-idle-symbolic',
icon_size: 16 });
this._container.add_actor(this._icon);
this._label = new St.Label();
this._container.add_actor(this._label);
this._highlight = new St.Widget({ style_class: 'messages-indicator-highlight',
x_expand: true,
y_expand: true,
y_align: Clutter.ActorAlign.END,
visible: false });
this._container.connect('notify::hover', Lang.bind(this,
function() {
this._highlight.visible = this._container.hover;
}));
let clickAction = new Clutter.ClickAction();
this._container.add_action(clickAction);
clickAction.connect('clicked', Lang.bind(this,
function() {
Main.messageTray.openTray();
}));
Main.messageTray.connect('showing', Lang.bind(this,
function() {
this._highlight.visible = false;
this._container.hover = false;
}));
let layout = new Clutter.BinLayout();
this.actor = new St.Widget({ layout_manager: layout,
style_class: 'messages-indicator',
y_expand: true,
y_align: Clutter.ActorAlign.END,
visible: false });
this.actor.add_actor(this._container);
this.actor.add_actor(this._highlight);
Main.messageTray.connect('source-added', Lang.bind(this, this._onSourceAdded));
Main.messageTray.connect('source-removed', Lang.bind(this, this._onSourceRemoved));
let sources = Main.messageTray.getSources();
sources.forEach(Lang.bind(this, function(source) { this._onSourceAdded(null, source); }));
this._viewSelector.connect('page-changed', Lang.bind(this, this._updateVisibility));
Main.overview.connect('showing', Lang.bind(this, this._updateVisibility));
},
_onSourceAdded: function(tray, source) {
if (source.trayIcon)
return;
if (source.isTransient)
return;
source.connect('count-updated', Lang.bind(this, this._updateCount));
this._sources.push(source);
this._updateCount();
},
_onSourceRemoved: function(tray, source) {
this._sources.splice(this._sources.indexOf(source), 1);
this._updateCount();
},
_updateCount: function() {
let count = 0;
this._sources.forEach(Lang.bind(this,
function(source) {
count += source.indicatorCount;
}));
this._count = count;
this._label.text = ngettext("%d new message",
"%d new messages",
count).format(count);
this._updateVisibility();
},
_updateVisibility: function() {
let activePage = this._viewSelector.getActivePage();
let visible = ((this._count > 0) && (activePage == ViewSelector.ViewPage.WINDOWS));
this.actor.visible = visible;
}
});
const ControlsManager = new Lang.Class({
Name: 'ControlsManager',
_init: function(dash, thumbnails, viewSelector) {
this._dashSlider = new DashSlider(dash);
this.dashActor = this._dashSlider.actor;
this.dashSpacer = new DashSpacer();
this.dashSpacer.setDashActor(this.dashActor);
this._thumbnailsSlider = new ThumbnailsSlider(thumbnails);
this.thumbnailsActor = this._thumbnailsSlider.actor;
this._indicator = new MessagesIndicator(viewSelector);
this.indicatorActor = this._indicator.actor;
this._viewSelector = viewSelector;
this._viewSelector.connect('page-changed', Lang.bind(this, this._setVisibility));
this._viewSelector.connect('page-empty', Lang.bind(this, this._onPageEmpty));
Main.overview.connect('showing', Lang.bind(this, this._updateSpacerVisibility));
Main.overview.connect('item-drag-begin', Lang.bind(this,
function() {
let activePage = this._viewSelector.getActivePage();
if (activePage != ViewSelector.ViewPage.WINDOWS)
this._viewSelector.fadeHalf();
}));
Main.overview.connect('item-drag-end', Lang.bind(this,
function() {
this._viewSelector.fadeIn();
}));
Main.overview.connect('item-drag-cancelled', Lang.bind(this,
function() {
this._viewSelector.fadeIn();
}));
},
_setVisibility: function() {
// Ignore the case when we're leaving the overview, since
// actors will be made visible again when entering the overview
// next time, and animating them while doing so is just
// unnecessary noise
if (!Main.overview.visible ||
(Main.overview.animationInProgress && !Main.overview.visibleTarget))
return;
let activePage = this._viewSelector.getActivePage();
let dashVisible = (activePage == ViewSelector.ViewPage.WINDOWS ||
activePage == ViewSelector.ViewPage.APPS);
let thumbnailsVisible = (activePage == ViewSelector.ViewPage.WINDOWS);
if (dashVisible)
this._dashSlider.slideIn();
else
this._dashSlider.slideOut();
if (thumbnailsVisible)
this._thumbnailsSlider.slideIn();
else
this._thumbnailsSlider.slideOut();
},
_updateSpacerVisibility: function() {
if (Main.overview.animationInProgress && !Main.overview.visibleTarget)
return;
let activePage = this._viewSelector.getActivePage();
this.dashSpacer.visible = (activePage == ViewSelector.ViewPage.WINDOWS);
},
_onPageEmpty: function() {
this._dashSlider.pageEmpty();
this._thumbnailsSlider.pageEmpty();
this._updateSpacerVisibility();
}
});

View File

@ -75,33 +75,41 @@ function _unpremultiply(color) {
blue: blue, alpha: color.alpha });
};
const Animation = new Lang.Class({
Name: 'Animation',
const AnimatedIcon = new Lang.Class({
Name: 'AnimatedIcon',
_init: function(name, size) {
this.actor = new St.Bin({ visible: false });
_init: function(filename, width, height, speed) {
this.actor = new St.Bin();
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this.actor.connect('notify::visible', Lang.bind(this, this._onVisibleNotify));
this._speed = speed;
this._isLoaded = false;
this._isPlaying = false;
this._timeoutId = 0;
this._frame = 0;
this._animations = St.TextureCache.get_default().load_sliced_image (global.datadir + '/theme/' + name, size, size);
this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height,
Lang.bind(this, this._animationsLoaded));
this.actor.set_child(this._animations);
},
_disconnectTimeout: function() {
play: function() {
if (this._isLoaded && this._timeoutId == 0) {
if (this._frame == 0)
this._showFrame(0);
this._timeoutId = Mainloop.timeout_add(this._speed, Lang.bind(this, this._update));
}
this._isPlaying = true;
},
stop: function() {
if (this._timeoutId > 0) {
Mainloop.source_remove(this._timeoutId);
this._timeoutId = 0;
}
},
_onVisibleNotify: function() {
if (this.actor.visible)
this._timeoutId = Mainloop.timeout_add(ANIMATED_ICON_UPDATE_TIMEOUT, Lang.bind(this, this._update));
else
this._disconnectTimeout();
this._isPlaying = false;
},
_showFrame: function(frame) {
@ -121,8 +129,24 @@ const AnimatedIcon = new Lang.Class({
return true;
},
_animationsLoaded: function() {
this._isLoaded = true;
if (this._isPlaying)
this.play();
},
_onDestroy: function() {
this._disconnectTimeout();
this.stop();
}
});
const AnimatedIcon = new Lang.Class({
Name: 'AnimatedIcon',
Extends: Animation,
_init: function(name, size) {
this.parent(global.datadir + '/theme/' + name, size, size, ANIMATED_ICON_UPDATE_TIMEOUT);
}
});
@ -179,7 +203,7 @@ const TextShadower = new Lang.Class({
let child = children[i];
let childBox = new Clutter.ActorBox();
// The order of the labels here is arbitrary, except
// we know the "real" label is at the end because Clutter.Group
// we know the "real" label is at the end because Clutter.Actor
// sorts by Z order
switch (i) {
case 0: // top
@ -277,6 +301,7 @@ const AppMenuButton = new Lang.Class({
this._spinner = new AnimatedIcon('process-working.svg',
PANEL_ICON_SIZE);
this._container.add_actor(this._spinner.actor);
this._spinner.actor.hide();
this._spinner.actor.lower_bottom();
let tracker = Shell.WindowTracker.get_default();
@ -290,7 +315,7 @@ const AppMenuButton = new Lang.Class({
},
show: function() {
if (this._visible || Main.screenShield.locked)
if (this._visible)
return;
this._visible = true;
@ -367,6 +392,7 @@ const AppMenuButton = new Lang.Class({
transition: "easeOutQuad",
onCompleteScope: this,
onComplete: function() {
this._spinner.stop();
this._spinner.actor.opacity = 255;
this._spinner.actor.hide();
}
@ -376,6 +402,7 @@ const AppMenuButton = new Lang.Class({
startAnimation: function() {
this._stop = false;
this.actor.reactive = false;
this._spinner.play();
this._spinner.actor.show();
},
@ -571,12 +598,18 @@ const AppMenuButton = new Lang.Class({
return;
menu = new PopupMenu.RemoteMenu(this.actor, this._targetApp.menu, this._targetApp.action_group);
menu.connect('activate', Lang.bind(this, function() {
let win = this._targetApp.get_windows()[0];
win.check_alive(global.get_current_time());
}));
} else {
if (this.menu && !(this.menu instanceof PopupMenu.RemoteMenu))
if (this.menu.isDummyQuitMenu)
return;
// fallback to older menu
menu = new PopupMenu.PopupMenu(this.actor, 0.0, St.Side.TOP, 0);
menu.isDummyQuitMenu = true;
menu.addAction(_("Quit"), Lang.bind(this, function() {
this._targetApp.request_quit();
}));
@ -589,15 +622,12 @@ const AppMenuButton = new Lang.Class({
Signals.addSignalMethods(AppMenuButton.prototype);
// Activities button. Because everything else in the top bar is a
// PanelMenu.Button, it simplifies some things to make this be one too.
// We just hack it up to not actually have a menu attached to it.
const ActivitiesButton = new Lang.Class({
Name: 'ActivitiesButton',
Extends: PanelMenu.Button,
_init: function() {
this.parent(0.0);
this.parent(0.0, null, true);
this.actor.accessible_role = Atk.Role.TOGGLE_BUTTON;
let container = new Shell.GenericContainer();
@ -614,30 +644,30 @@ const ActivitiesButton = new Lang.Class({
this.actor.label_actor = this._label;
this.hotCorner = new Layout.HotCorner();
this.hotCorner = new Layout.HotCorner(Main.layoutManager);
container.add_actor(this.hotCorner.actor);
// Hack up our menu...
this.menu.open = Lang.bind(this, this._onMenuOpenRequest);
this.menu.close = Lang.bind(this, this._onMenuCloseRequest);
this.menu.toggle = Lang.bind(this, this._onMenuToggleRequest);
this.actor.connect('captured-event', Lang.bind(this, this._onCapturedEvent));
this.actor.connect_after('button-release-event', Lang.bind(this, this._onButtonRelease));
this.actor.connect_after('key-release-event', Lang.bind(this, this._onKeyRelease));
Main.overview.connect('showing', Lang.bind(this, function() {
this.actor.add_style_pseudo_class('overview');
this._escapeMenuGrab();
this.actor.add_accessible_state (Atk.StateType.CHECKED);
}));
Main.overview.connect('hiding', Lang.bind(this, function() {
this.actor.remove_style_pseudo_class('overview');
this._escapeMenuGrab();
this.actor.remove_accessible_state (Atk.StateType.CHECKED);
}));
this._xdndTimeOut = 0;
// Since the hot corner uses stage coordinates, Clutter won't
// queue relayouts for us when the panel moves. Queue a relayout
// when that happens.
Main.layoutManager.connect('panel-box-changed', Lang.bind(this, function() {
container.queue_relayout();
}));
},
_containerGetPreferredWidth: function(actor, forHeight, alloc) {
@ -683,11 +713,6 @@ const ActivitiesButton = new Lang.Class({
return DND.DragMotionResult.CONTINUE;
},
_escapeMenuGrab: function() {
if (this.menu.isOpen)
this.menu.close();
},
_onCapturedEvent: function(actor, event) {
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
if (!this.hotCorner.shouldToggleOverviewOnClick())
@ -696,33 +721,13 @@ const ActivitiesButton = new Lang.Class({
return false;
},
_onMenuOpenRequest: function() {
this.menu.isOpen = true;
this.menu.emit('open-state-changed', true);
},
_onMenuCloseRequest: function() {
this.menu.isOpen = false;
this.menu.emit('open-state-changed', false);
},
_onMenuToggleRequest: function() {
this.menu.isOpen = !this.menu.isOpen;
this.menu.emit('open-state-changed', this.menu.isOpen);
},
_onButtonRelease: function() {
if (this.menu.isOpen) {
this.menu.close();
Main.overview.toggle();
}
Main.overview.toggle();
},
_onKeyRelease: function(actor, event) {
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) {
if (this.menu.isOpen)
this.menu.close();
Main.overview.toggle();
}
},
@ -734,7 +739,6 @@ const ActivitiesButton = new Lang.Class({
if (pickedActor == this.actor) {
if (!Main.overview.visible && !Main.overview.animationInProgress) {
Main.overview.showTemporarily();
Main.overview.beginItemDrag(actor);
}
}
@ -746,12 +750,9 @@ const ActivitiesButton = new Lang.Class({
const PanelCorner = new Lang.Class({
Name: 'PanelCorner',
_init: function(box, side) {
_init: function(side) {
this._side = side;
this._box = box;
this._box.connect('style-changed', Lang.bind(this, this._boxStyleChanged));
this.actor = new St.DrawingArea({ style_class: 'panel-corner' });
this.actor.connect('style-changed', Lang.bind(this, this._styleChanged));
this.actor.connect('repaint', Lang.bind(this, this._repaint));
@ -807,12 +808,12 @@ const PanelCorner = new Lang.Class({
return children[index];
},
_boxStyleChanged: function() {
setStyleParent: function(box) {
let side = this._side;
let rtlAwareContainer = this._box instanceof St.BoxLayout;
let rtlAwareContainer = box instanceof St.BoxLayout;
if (rtlAwareContainer &&
this._box.get_text_direction() == Clutter.TextDirection.RTL) {
box.get_text_direction() == Clutter.TextDirection.RTL) {
if (this._side == St.Side.LEFT)
side = St.Side.RIGHT;
else if (this._side == St.Side.RIGHT)
@ -821,9 +822,9 @@ const PanelCorner = new Lang.Class({
let button;
if (side == St.Side.LEFT)
button = this._findLeftmostButton(this._box);
button = this._findLeftmostButton(box);
else if (side == St.Side.RIGHT)
button = this._findRightmostButton(this._box);
button = this._findRightmostButton(box);
if (button) {
if (this._button && this._buttonStyleChangedSignalId) {
@ -850,7 +851,7 @@ const PanelCorner = new Lang.Class({
// The corner doesn't support theme transitions, so override
// the .panel-button default
button.style = 'transition-duration: 0';
button.style = 'transition-duration: 0ms';
}
},
@ -863,10 +864,13 @@ const PanelCorner = new Lang.Class({
let backgroundColor = node.get_color('-panel-corner-background-color');
let borderColor = node.get_color('-panel-corner-border-color');
let overlap = borderColor.alpha != 0;
let offsetY = overlap ? 0 : borderWidth;
let cr = this.actor.get_context();
cr.setOperator(Cairo.Operator.SOURCE);
cr.moveTo(0, 0);
cr.moveTo(0, offsetY);
if (this._side == St.Side.LEFT)
cr.arc(cornerRadius,
borderWidth + cornerRadius,
@ -875,7 +879,7 @@ const PanelCorner = new Lang.Class({
cr.arc(0,
borderWidth + cornerRadius,
cornerRadius, 3 * Math.PI / 2, 2 * Math.PI);
cr.lineTo(cornerRadius, 0);
cr.lineTo(cornerRadius, offsetY);
cr.closePath();
let savedPath = cr.copyPath();
@ -885,14 +889,18 @@ const PanelCorner = new Lang.Class({
Clutter.cairo_set_source_color(cr, over);
cr.fill();
let offset = borderWidth;
Clutter.cairo_set_source_color(cr, backgroundColor);
if (overlap) {
let offset = borderWidth;
Clutter.cairo_set_source_color(cr, backgroundColor);
cr.save();
cr.translate(xOffsetDirection * offset, - offset);
cr.appendPath(savedPath);
cr.fill();
cr.restore();
cr.save();
cr.translate(xOffsetDirection * offset, - offset);
cr.appendPath(savedPath);
cr.fill();
cr.restore();
}
cr.$dispose();
},
_styleChanged: function() {
@ -911,6 +919,7 @@ const PANEL_ITEM_IMPLEMENTATIONS = {
'appMenu': AppMenuButton,
'dateMenu': imports.ui.dateMenu.DateMenuButton,
'a11y': imports.ui.status.accessibility.ATIndicator,
'a11yGreeter': imports.ui.status.accessibility.ATGreeterIndicator,
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
@ -939,14 +948,9 @@ const Panel = new Lang.Class({
reactive: true });
this.actor._delegate = this;
this.statusArea = {};
this._sessionStyle = null;
Main.overview.connect('shown', Lang.bind(this, function () {
this.actor.add_style_class_name('in-overview');
}));
Main.overview.connect('hiding', Lang.bind(this, function () {
this.actor.remove_style_class_name('in-overview');
}));
this.statusArea = {};
this.menuManager = new PopupMenu.PopupMenuManager(this);
@ -957,17 +961,10 @@ const Panel = new Lang.Class({
this._rightBox = new St.BoxLayout({ name: 'panelRight' });
this.actor.add_actor(this._rightBox);
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
this._leftCorner = new PanelCorner(this._rightBox, St.Side.LEFT);
else
this._leftCorner = new PanelCorner(this._leftBox, St.Side.LEFT);
this._leftCorner = new PanelCorner(St.Side.LEFT);
this.actor.add_actor(this._leftCorner.actor);
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL)
this._rightCorner = new PanelCorner(this._leftBox, St.Side.RIGHT);
else
this._rightCorner = new PanelCorner(this._rightBox, St.Side.RIGHT);
this._rightCorner = new PanelCorner(St.Side.RIGHT);
this.actor.add_actor(this._rightCorner.actor);
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
@ -975,8 +972,15 @@ const Panel = new Lang.Class({
this.actor.connect('allocate', Lang.bind(this, this._allocate));
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
Main.overview.connect('showing', Lang.bind(this, function () {
this.actor.add_style_pseudo_class('overview');
}));
Main.overview.connect('hiding', Lang.bind(this, function () {
this.actor.remove_style_pseudo_class('overview');
}));
Main.layoutManager.panelBox.add(this.actor);
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here-symbolic',
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'emblem-system-symbolic',
{ sortGroup: CtrlAltTab.SortGroup.TOP });
Main.sessionMode.connect('updated', Lang.bind(this, this._updatePanel));
@ -1061,6 +1065,9 @@ const Panel = new Lang.Class({
},
_onButtonPress: function(actor, event) {
if (Main.modalCount > 0)
return false;
if (event.get_source() != actor)
return false;
@ -1133,17 +1140,20 @@ const Panel = new Lang.Class({
this._updateBox(panel.left, this._leftBox);
this._updateBox(panel.center, this._centerBox);
this._updateBox(panel.right, this._rightBox);
},
_initBox: function(elements, box) {
for (let i = 0; i < elements.length; i++) {
let role = elements[i];
let constructor = PANEL_ITEM_IMPLEMENTATIONS[role];
if (!constructor) {
// panel icon is not supported (can happen for
// bluetooth or network)
continue;
}
if (this._sessionStyle)
this._removeStyleClassName(this._sessionStyle);
this._sessionStyle = Main.sessionMode.panelStyle;
if (this._sessionStyle)
this._addStyleClassName(this._sessionStyle);
if (this.actor.get_text_direction() == Clutter.TextDirection.RTL) {
this._leftCorner.setStyleParent(this._rightBox);
this._rightCorner.setStyleParent(this._leftBox);
} else {
this._leftCorner.setStyleParent(this._leftBox);
this._rightCorner.setStyleParent(this._rightBox);
}
},
@ -1221,5 +1231,17 @@ const Panel = new Lang.Class({
this.statusArea[role] = indicator;
this._addToPanelBox(role, indicator, position, boxContainer);
return indicator;
},
_addStyleClassName: function(className) {
this.actor.add_style_class_name(className);
this._rightCorner.actor.add_style_class_name(className);
this._leftCorner.actor.add_style_class_name(className);
},
_removeStyleClassName: function(className) {
this.actor.remove_style_class_name(className);
this._rightCorner.actor.remove_style_class_name(className);
this._leftCorner.actor.remove_style_class_name(className);
}
});

View File

@ -112,7 +112,7 @@ const Button = new Lang.Class({
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
if (dontCreateMenu)
this.menu = null;
this.menu = new PopupMenu.PopupDummyMenu(this.actor);
else
this.setMenu(new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, 0));
@ -205,10 +205,8 @@ const Button = new Lang.Class({
// Setting the max-height won't do any good if the minimum height of the
// menu is higher then the screen; it's useful if part of the menu is
// scrollable so the minimum height is smaller than the natural height
let monitor = Main.layoutManager.primaryMonitor;
this.menu.actor.style = ('max-height: ' +
Math.round(monitor.height - Main.panel.actor.height) +
'px;');
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
this.menu.actor.style = ('max-height: ' + Math.round(workArea.height) + 'px;');
},
destroy: function() {
@ -244,18 +242,21 @@ const SystemStatusButton = new Lang.Class({
this.setIcon(iconName);
},
get icons() {
return this._box.get_children();
},
addIcon: function(gicon) {
let icon = new St.Icon({ gicon: gicon,
style_class: 'system-status-icon' });
this._box.add_actor(icon);
this.emit('icons-changed');
return icon;
},
setIcon: function(iconName) {
// Need to first add a NULL GIcon and then set icon_name, to ensure
// compatibility with -symbolic fallbacks
if (!this.mainIcon)
this.mainIcon = this.addIcon(null);
this.mainIcon.icon_name = iconName;

View File

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

View File

@ -1,6 +1,5 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Cairo = imports.cairo;
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
@ -12,8 +11,10 @@ const St = imports.gi.St;
const Atk = imports.gi.Atk;
const BoxPointer = imports.ui.boxpointer;
const GrabHelper = imports.ui.grabHelper;
const Main = imports.ui.main;
const Params = imports.misc.params;
const Separator = imports.ui.separator;
const Tweener = imports.ui.tweener;
const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */
@ -206,6 +207,7 @@ const PopupBaseMenuItem = new Lang.Class({
color.alpha / 255);
cr.arc(width / 2, height / 2, width / 3, 0, 2 * Math.PI);
cr.fill();
cr.$dispose();
},
// This returns column widths in logical order (i.e. from the dot
@ -404,29 +406,8 @@ 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));
},
_onRepaint: function(area) {
let cr = area.get_context();
let themeNode = area.get_theme_node();
let [width, height] = area.get_surface_size();
let margin = themeNode.get_length('-margin-horizontal');
let gradientHeight = themeNode.get_length('-gradient-height');
let startColor = themeNode.get_color('-gradient-start');
let endColor = themeNode.get_color('-gradient-end');
let gradientWidth = (width - margin * 2);
let gradientOffset = (height - gradientHeight) / 2;
let pattern = new Cairo.LinearGradient(margin, gradientOffset, width - margin, gradientOffset + gradientHeight);
pattern.addColorStopRGBA(0, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255);
pattern.addColorStopRGBA(0.5, endColor.red / 255, endColor.green / 255, endColor.blue / 255, endColor.alpha / 255);
pattern.addColorStopRGBA(1, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255);
cr.setSource(pattern);
cr.rectangle(margin, gradientOffset, gradientWidth, gradientHeight);
cr.fill();
this._separator = new Separator.HorizontalSeparator({ style_class: 'popup-separator-menu-item' });
this.addActor(this._separator.actor, { span: -1, expand: true });
}
});
@ -624,6 +605,7 @@ const PopupSliderMenuItem = new Lang.Class({
color.alpha / 255);
cr.arc(handleX, handleY, handleRadius, 0, 2 * Math.PI);
cr.fill();
cr.$dispose();
},
_startDragging: function(actor, event) {
@ -656,20 +638,34 @@ const PopupSliderMenuItem = new Lang.Class({
return true;
},
_onScrollEvent: function (actor, event) {
scroll: function(event) {
let direction = event.get_scroll_direction();
let delta;
if (event.is_pointer_emulated())
return;
if (direction == Clutter.ScrollDirection.DOWN) {
this._value = Math.max(0, this._value - SLIDER_SCROLL_STEP);
}
else if (direction == Clutter.ScrollDirection.UP) {
this._value = Math.min(1, this._value + SLIDER_SCROLL_STEP);
delta = -SLIDER_SCROLL_STEP;
} else if (direction == Clutter.ScrollDirection.UP) {
delta = +SLIDER_SCROLL_STEP;
} else if (direction == Clutter.ScrollDirection.SMOOTH) {
let [dx, dy] = event.get_scroll_delta();
// Even though the slider is horizontal, use dy to match
// the UP/DOWN above.
delta = -dy / 10;
}
this._value = Math.min(Math.max(0, this._value + delta), 1);
this._slider.queue_repaint();
this.emit('value-changed', this._value);
},
_onScrollEvent: function(actor, event) {
this.scroll(event);
},
_motionEvent: function(actor, event) {
let absX, absY;
[absX, absY] = event.get_coords();
@ -871,7 +867,6 @@ const PopupMenuBase = new Lang.Class({
this.blockSourceEvents = false;
this._activeMenuItem = null;
this._childMenus = [];
this._settingsActions = { };
this._sessionUpdatedId = Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
@ -893,7 +888,7 @@ const PopupMenuBase = new Lang.Class({
addSettingsAction: function(title, desktopFile) {
let menuItem = this.addAction(title, function() {
let app = Shell.AppSystem.get_default().lookup_setting(desktopFile);
let app = Shell.AppSystem.get_default().lookup_app(desktopFile);
if (!app) {
log('Settings panel for desktop file ' + desktopFile + ' could not be loaded!');
@ -918,29 +913,15 @@ const PopupMenuBase = new Lang.Class({
},
isEmpty: function() {
return this.box.get_n_children() == 0;
let hasVisibleChildren = this.box.get_children().some(function(child) {
return child.visible;
});
return !hasVisibleChildren;
},
isChildMenu: function(menu) {
return this._childMenus.indexOf(menu) != -1;
},
addChildMenu: function(menu) {
if (this.isChildMenu(menu))
return;
this._childMenus.push(menu);
this.emit('child-menu-added', menu);
},
removeChildMenu: function(menu) {
let index = this._childMenus.indexOf(menu);
if (index == -1)
return;
this._childMenus.splice(index, 1);
this.emit('child-menu-removed', menu);
isChildMenu: function() {
return false;
},
/**
@ -1204,7 +1185,6 @@ const PopupMenu = new Lang.Class({
this.actor = this._boxPointer.actor;
this.actor._delegate = this;
this.actor.style_class = 'popup-menu-boxpointer';
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
this._boxWrapper = new Shell.GenericContainer();
this._boxWrapper.connect('get-preferred-width', Lang.bind(this, this._boxGetPreferredWidth));
@ -1216,6 +1196,8 @@ const PopupMenu = new Lang.Class({
global.focus_manager.add_group(this.actor);
this.actor.reactive = true;
this._childMenus = [];
},
_boxGetPreferredWidth: function (actor, forHeight, alloc) {
@ -1234,15 +1216,6 @@ const PopupMenu = new Lang.Class({
this.box.allocate(box, flags);
},
_onKeyPressEvent: function(actor, event) {
if (event.get_key_symbol() == Clutter.Escape) {
this.close(BoxPointer.PopupAnimation.FULL);
return true;
}
return false;
},
setArrowOrigin: function(origin) {
this._boxPointer.setArrowOrigin(origin);
},
@ -1251,6 +1224,28 @@ const PopupMenu = new Lang.Class({
this._boxPointer.setSourceAlignment(alignment);
},
isChildMenu: function(menu) {
return this._childMenus.indexOf(menu) != -1;
},
addChildMenu: function(menu) {
if (this.isChildMenu(menu))
return;
this._childMenus.push(menu);
this.emit('child-menu-added', menu);
},
removeChildMenu: function(menu) {
let index = this._childMenus.indexOf(menu);
if (index == -1)
return;
this._childMenus.splice(index, 1);
this.emit('child-menu-removed', menu);
},
open: function(animate) {
if (this.isOpen)
return;
@ -1272,6 +1267,10 @@ const PopupMenu = new Lang.Class({
if (this._activeMenuItem)
this._activeMenuItem.setActive(false);
this._childMenus.forEach(function(childMenu) {
childMenu.close();
});
if (this._boxPointer.actor.visible)
this._boxPointer.hide(animate);
@ -1283,6 +1282,28 @@ const PopupMenu = new Lang.Class({
}
});
const PopupDummyMenu = new Lang.Class({
Name: 'PopupDummyMenu',
_init: function(sourceActor) {
this.sourceActor = sourceActor;
this.actor = sourceActor;
this.actor._delegate = this;
},
isChildMenu: function() {
return false;
},
open: function() { this.emit('open-state-changed', true); },
close: function() { this.emit('open-state-changed', false); },
toggle: function() {},
destroy: function() {
this.emit('destroy');
},
});
Signals.addSignalMethods(PopupDummyMenu.prototype);
const PopupSubMenu = new Lang.Class({
Name: 'PopupSubMenu',
Extends: PopupMenuBase,
@ -1537,7 +1558,6 @@ const PopupComboMenu = new Lang.Class({
this.actor = this.box;
this.actor._delegate = this;
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
sourceActor.connect('style-changed',
Lang.bind(this, this._onSourceActorStyleChanged));
@ -1545,15 +1565,6 @@ const PopupComboMenu = new Lang.Class({
global.focus_manager.add_group(this.actor);
},
_onKeyPressEvent: function(actor, event) {
if (event.get_key_symbol() == Clutter.Escape) {
this.close(BoxPointer.PopupAnimation.FULL);
return true;
}
return false;
},
_onKeyFocusIn: function(actor) {
let items = this._getMenuItems();
let activeItem = items[this._activeItemPos];
@ -1680,9 +1691,7 @@ const PopupComboBoxMenuItem = new Lang.Class({
_getTopMenu: function() {
let actor = this.actor.get_parent();
while (actor) {
if (actor._delegate &&
(actor._delegate instanceof PopupMenu ||
actor._delegate instanceof PopupComboMenu))
if (actor._delegate && actor._delegate instanceof PopupMenu)
return actor._delegate;
actor = actor.get_parent();
@ -2044,17 +2053,8 @@ const PopupMenuManager = new Lang.Class({
_init: function(owner) {
this._owner = owner;
this.grabbed = false;
this._eventCaptureId = 0;
this._enterEventId = 0;
this._leaveEventId = 0;
this._keyFocusNotifyId = 0;
this._activeMenu = null;
this._grabHelper = new GrabHelper.GrabHelper(owner.actor);
this._menus = [];
this._menuStack = [];
this._preGrabInputMode = null;
this._grabbedFromKeynav = false;
},
addMenu: function(menu, position) {
@ -2073,6 +2073,8 @@ const PopupMenuManager = new Lang.Class({
let source = menu.sourceActor;
if (source) {
if (!menu.blockSourceEvents)
this._grabHelper.addActor(source);
menudata.enterId = source.connect('enter-event', Lang.bind(this, function() { this._onMenuSourceEnter(menu); }));
menudata.focusInId = source.connect('key-focus-in', Lang.bind(this, function() { this._onMenuSourceEnter(menu); }));
}
@ -2085,7 +2087,7 @@ const PopupMenuManager = new Lang.Class({
removeMenu: function(menu) {
if (menu == this._activeMenu)
this._closeMenu();
this._closeMenu(menu);
let position = this._findMenu(menu);
if (position == -1) // not a menu we manage
@ -2102,79 +2104,29 @@ const PopupMenuManager = new Lang.Class({
if (menudata.focusInId)
menu.sourceActor.disconnect(menudata.focusInId);
if (menu.sourceActor)
this._grabHelper.removeActor(menu.sourceActor);
this._menus.splice(position, 1);
},
_grab: function() {
Main.pushModal(this._owner.actor);
this._eventCaptureId = global.stage.connect('captured-event', Lang.bind(this, this._onEventCapture));
// captured-event doesn't see enter/leave events
this._enterEventId = global.stage.connect('enter-event', Lang.bind(this, this._onEventCapture));
this._leaveEventId = global.stage.connect('leave-event', Lang.bind(this, this._onEventCapture));
this._keyFocusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
this.grabbed = true;
get activeMenu() {
let actor = this._grabHelper.currentGrab.actor;
if (actor)
return actor._delegate;
else
return null;
},
_ungrab: function() {
global.stage.disconnect(this._eventCaptureId);
this._eventCaptureId = 0;
global.stage.disconnect(this._enterEventId);
this._enterEventId = 0;
global.stage.disconnect(this._leaveEventId);
this._leaveEventId = 0;
global.stage.disconnect(this._keyFocusNotifyId);
this._keyFocusNotifyId = 0;
this.grabbed = false;
Main.popModal(this._owner.actor);
ignoreRelease: function() {
return this._grabHelper.ignoreRelease();
},
_onMenuOpenState: function(menu, open) {
if (open) {
if (this._activeMenu && this._activeMenu.isChildMenu(menu)) {
this._menuStack.push(this._activeMenu);
menu.actor.grab_key_focus();
}
this._activeMenu = menu;
this._grabHelper.grab({ actor: menu.actor, modal: true, focus: menu.sourceActor,
onUngrab: Lang.bind(this, this._closeMenu, menu) });
} else {
if (this._menuStack.length > 0) {
this._activeMenu = this._menuStack.pop();
if (menu.sourceActor)
menu.sourceActor.grab_key_focus();
this._didPop = true;
}
}
// Check what the focus was before calling pushModal/popModal
let focus = global.stage.key_focus;
let hadFocus = focus && this._activeMenuContains(focus);
if (open) {
if (!this.grabbed) {
this._preGrabInputMode = global.stage_input_mode;
this._grabbedFromKeynav = hadFocus;
this._grab();
}
if (hadFocus)
focus.grab_key_focus();
else
menu.actor.grab_key_focus();
} else if (menu == this._activeMenu) {
if (this.grabbed)
this._ungrab();
this._activeMenu = null;
if (this._grabbedFromKeynav) {
if (this._preGrabInputMode == Shell.StageInputMode.FOCUSED)
global.stage_input_mode = Shell.StageInputMode.FOCUSED;
if (hadFocus && menu.sourceActor)
menu.sourceActor.grab_key_focus();
else if (focus)
focus.grab_key_focus();
}
this._grabHelper.ungrab({ actor: menu.actor });
}
},
@ -2186,87 +2138,38 @@ const PopupMenuManager = new Lang.Class({
this.removeMenu(childMenu);
},
// change the currently-open menu without dropping grab
_changeMenu: function(newMenu) {
if (this._activeMenu) {
// _onOpenMenuState will drop the grab if it sees
// this._activeMenu being closed; so clear _activeMenu
// before closing it to keep that from happening
let oldMenu = this._activeMenu;
this._activeMenu = null;
for (let i = this._menuStack.length - 1; i >= 0; i--)
this._menuStack[i].close(BoxPointer.PopupAnimation.FADE);
let oldMenu = this.activeMenu;
if (oldMenu) {
oldMenu.close(BoxPointer.PopupAnimation.FADE);
newMenu.open(BoxPointer.PopupAnimation.FADE);
} else
} else {
newMenu.open(BoxPointer.PopupAnimation.FULL);
}
},
_onMenuSourceEnter: function(menu) {
if (!this.grabbed || menu == this._activeMenu)
if (!this._grabHelper.grabbed)
return false;
if (this._activeMenu && this._activeMenu.isChildMenu(menu))
if (this._grabHelper.isActorGrabbed(menu.actor))
return false;
if (this._menuStack.indexOf(menu) != -1)
return false;
if (this._menuStack.length > 0 && this._menuStack[0].isChildMenu(menu))
let isChildMenu = this._grabHelper.grabStack.some(function(grab) {
let existingMenu = grab.actor._delegate;
return existingMenu.isChildMenu(menu);
});
if (isChildMenu)
return false;
this._changeMenu(menu);
return false;
},
_onKeyFocusChanged: function() {
if (!this.grabbed || !this._activeMenu)
return;
let focus = global.stage.key_focus;
if (focus) {
if (this._activeMenuContains(focus))
return;
if (this._menuStack.length > 0)
return;
if (focus._delegate && focus._delegate.menu &&
this._findMenu(focus._delegate.menu) != -1)
return;
}
this._closeMenu();
},
_onMenuDestroy: function(menu) {
this.removeMenu(menu);
},
_activeMenuContains: function(actor) {
return this._activeMenu != null
&& (this._activeMenu.actor.contains(actor) ||
(this._activeMenu.sourceActor && this._activeMenu.sourceActor.contains(actor)));
},
_eventIsOnActiveMenu: function(event) {
return this._activeMenuContains(event.get_source());
},
_shouldBlockEvent: function(event) {
let src = event.get_source();
if (this._activeMenu != null && this._activeMenu.actor.contains(src))
return false;
for (let i = 0; i < this._menus.length; i++) {
let menu = this._menus[i].menu;
if (menu.sourceActor && !menu.blockSourceEvents && menu.sourceActor.contains(src)) {
return false;
}
}
return true;
},
_findMenu: function(item) {
for (let i = 0; i < this._menus.length; i++) {
let menudata = this._menus[i];
@ -2276,41 +2179,11 @@ const PopupMenuManager = new Lang.Class({
return -1;
},
_onEventCapture: function(actor, event) {
if (!this.grabbed)
return false;
if (this._owner.menuEventFilter &&
this._owner.menuEventFilter(event))
return true;
if (this._didPop) {
this._didPop = false;
return true;
}
let activeMenuContains = this._eventIsOnActiveMenu(event);
let eventType = event.type();
if (eventType == Clutter.EventType.BUTTON_RELEASE) {
if (activeMenuContains) {
return false;
} else {
this._closeMenu();
return true;
}
} else if (eventType == Clutter.EventType.BUTTON_PRESS && !activeMenuContains) {
this._closeMenu();
return true;
} else if (!this._shouldBlockEvent(event)) {
return false;
}
return true;
},
_closeMenu: function() {
if (this._activeMenu != null)
this._activeMenu.close(BoxPointer.PopupAnimation.FULL);
_closeMenu: function(isUser, menu) {
// If this isn't a user action, we called close()
// on the BoxPointer ourselves, so we shouldn't
// reanimate.
if (isUser)
menu.close(BoxPointer.PopupAnimation.FULL);
}
});

View File

@ -1,9 +1,11 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GdkPixbuf = imports.gi.GdkPixbuf;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const FileUtils = imports.misc.fileUtils;
const Search = imports.ui.search;
@ -29,105 +31,183 @@ const SearchProviderIface = <interface name="org.gnome.Shell.SearchProvider">
</method>
</interface>;
var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface);
const SearchProvider2Iface = <interface name="org.gnome.Shell.SearchProvider2">
<method name="GetInitialResultSet">
<arg type="as" direction="in" />
<arg type="as" direction="out" />
</method>
<method name="GetSubsearchResultSet">
<arg type="as" direction="in" />
<arg type="as" direction="in" />
<arg type="as" direction="out" />
</method>
<method name="GetResultMetas">
<arg type="as" direction="in" />
<arg type="aa{sv}" direction="out" />
</method>
<method name="ActivateResult">
<arg type="s" direction="in" />
<arg type="as" direction="in" />
<arg type="u" direction="in" />
</method>
<method name="LaunchSearch">
<arg type="as" direction="in" />
<arg type="u" direction="in" />
</method>
</interface>;
var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface);
var SearchProvider2Proxy = Gio.DBusProxy.makeProxyWrapper(SearchProvider2Iface);
function loadRemoteSearchProviders(addProviderCallback) {
let dataDirs = GLib.get_system_data_dirs();
let loadedProviders = {};
for (let i = 0; i < dataDirs.length; i++) {
let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', 'search-providers']);
let dir = Gio.file_new_for_path(path);
if (!dir.query_exists(null))
continue;
loadRemoteSearchProvidersFromDir(dir, loadedProviders, addProviderCallback);
let data = { loadedProviders: [],
objectPaths: {},
addProviderCallback: addProviderCallback };
FileUtils.collectFromDatadirsAsync('search-providers',
{ loadedCallback: remoteProvidersLoaded,
processFile: loadRemoteSearchProvider,
data: data
});
}
function loadRemoteSearchProvider(file, info, data) {
let keyfile = new GLib.KeyFile();
let path = file.get_path();
try {
keyfile.load_from_file(path, 0);
} catch(e) {
return;
}
};
function loadRemoteSearchProvidersFromDir(dir, loadedProviders, addProviderCallback) {
let dirPath = dir.get_path();
FileUtils.listDirAsync(dir, Lang.bind(this, function(files) {
for (let i = 0; i < files.length; i++) {
let keyfile = new GLib.KeyFile();
let path = GLib.build_filenamev([dirPath, files[i].get_name()]);
if (!keyfile.has_group(KEY_FILE_GROUP))
return;
try {
keyfile.load_from_file(path, 0);
} catch(e) {
continue;
}
let remoteProvider;
try {
let group = KEY_FILE_GROUP;
let busName = keyfile.get_string(group, 'BusName');
let objectPath = keyfile.get_string(group, 'ObjectPath');
if (!keyfile.has_group(KEY_FILE_GROUP))
continue;
if (data.objectPaths[objectPath])
return;
let remoteProvider, title;
try {
let group = KEY_FILE_GROUP;
let busName = keyfile.get_string(group, 'BusName');
let objectPath = keyfile.get_string(group, 'ObjectPath');
if (loadedProviders[objectPath])
continue;
let appInfo = null;
try {
let desktopId = keyfile.get_string(group, 'DesktopId');
appInfo = Gio.DesktopAppInfo.new(desktopId);
} catch (e) {
}
let icon;
if (appInfo) {
icon = appInfo.get_icon();
title = appInfo.get_name();
} else {
let iconName = keyfile.get_string(group, 'Icon');
icon = new Gio.ThemedIcon({ name: iconName });
title = keyfile.get_locale_string(group, 'Title', null);
}
remoteProvider = new RemoteSearchProvider(title,
icon,
busName,
objectPath);
loadedProviders[objectPath] = remoteProvider;
} catch(e) {
log('Failed to add search provider "%s": %s'.format(title, e.toString()));
continue;
}
addProviderCallback(remoteProvider);
let appInfo = null;
try {
let desktopId = keyfile.get_string(group, 'DesktopId');
appInfo = Gio.DesktopAppInfo.new(desktopId);
} catch (e) {
log('Ignoring search provider ' + path + ': missing DesktopId');
return;
}
}));
};
let version = '1';
try {
version = keyfile.get_string(group, 'Version');
} catch (e) {
// ignore error
}
if (version >= 2)
remoteProvider = new RemoteSearchProvider2(appInfo, busName, objectPath);
else
remoteProvider = new RemoteSearchProvider(appInfo, busName, objectPath);
data.objectPaths[objectPath] = remoteProvider;
data.loadedProviders.push(remoteProvider);
} catch(e) {
log('Failed to add search provider %s: %s'.format(path, e.toString()));
}
}
function remoteProvidersLoaded(loadState) {
let searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
let sortOrder = searchSettings.get_strv('sort-order');
// Special case gnome-control-center to be always active and always first
sortOrder.unshift('gnome-control-center.desktop');
let numSorted = sortOrder.length;
loadState.loadedProviders.sort(
function(providerA, providerB) {
let idxA, idxB;
let appIdA, appIdB;
appIdA = providerA.appInfo.get_id();
appIdB = providerB.appInfo.get_id();
idxA = sortOrder.indexOf(appIdA);
idxB = sortOrder.indexOf(appIdB);
// if no provider is found in the order, use alphabetical order
if ((idxA == -1) && (idxB == -1)) {
let nameA = providerA.appInfo.get_name();
let nameB = providerB.appInfo.get_name();
return GLib.utf8_collate(nameA, nameB);
}
if (numSorted > 1) {
// if providerA is the last, it goes after everything
if ((idxA + 1) == numSorted)
return 1;
// if providerB is the last, it goes after everything
else if ((idxB + 1) == numSorted)
return -1;
}
// if providerA isn't found, it's sorted after providerB
if (idxA == -1)
return 1;
// if providerB isn't found, it's sorted after providerA
if (idxB == -1)
return -1;
// finally, if both providers are found, return their order in the list
return (idxA - idxB);
});
loadState.loadedProviders.forEach(
function(provider) {
loadState.addProviderCallback(provider);
});
}
const RemoteSearchProvider = new Lang.Class({
Name: 'RemoteSearchProvider',
Extends: Search.SearchProvider,
_init: function(title, icon, dbusName, dbusPath) {
this._proxy = new SearchProviderProxy(Gio.DBus.session,
dbusName, dbusPath);
_init: function(appInfo, dbusName, dbusPath, proxyType) {
if (!proxyType)
proxyType = SearchProviderProxy;
this.proxy = new proxyType(Gio.DBus.session,
dbusName, dbusPath, Lang.bind(this, this._onProxyConstructed));
this.appInfo = appInfo;
this.id = appInfo.get_id();
this.isRemoteProvider = true;
this.parent(title.toUpperCase());
this._cancellable = new Gio.Cancellable();
},
_onProxyConstructed: function(proxy) {
// Do nothing
},
createIcon: function(size, meta) {
let gicon;
if (meta['gicon']) {
return new St.Icon({ gicon: Gio.icon_new_for_string(meta['gicon']),
icon_size: size });
gicon = Gio.icon_new_for_string(meta['gicon']);
} else if (meta['icon-data']) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = meta['icon-data'];
let textureCache = St.TextureCache.get_default();
return textureCache.load_from_raw(data, hasAlpha,
width, height, rowStride, size);
gicon = Shell.util_create_pixbuf_from_data(data, GdkPixbuf.Colorspace.RGB, hasAlpha,
bitsPerSample, width, height, rowStride);
}
// Ugh, but we want to fall back to something ...
return new St.Icon({ icon_name: 'text-x-generic',
return new St.Icon({ gicon: gicon,
icon_size: size });
},
@ -141,11 +221,11 @@ const RemoteSearchProvider = new Lang.Class({
this._cancellable.cancel();
this._cancellable.reset();
try {
this._proxy.GetInitialResultSetRemote(terms,
Lang.bind(this, this._getResultsFinished),
this._cancellable);
this.proxy.GetInitialResultSetRemote(terms,
Lang.bind(this, this._getResultsFinished),
this._cancellable);
} catch(e) {
log('Error calling GetInitialResultSet for provider %s: %s'.format( this.title, e.toString()));
log('Error calling GetInitialResultSet for provider %s: %s'.format(this.id, e.toString()));
this.searchSystem.pushResults(this, []);
}
},
@ -154,11 +234,11 @@ const RemoteSearchProvider = new Lang.Class({
this._cancellable.cancel();
this._cancellable.reset();
try {
this._proxy.GetSubsearchResultSetRemote(previousResults, newTerms,
Lang.bind(this, this._getResultsFinished),
this._cancellable);
this.proxy.GetSubsearchResultSetRemote(previousResults, newTerms,
Lang.bind(this, this._getResultsFinished),
this._cancellable);
} catch(e) {
log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.title, e.toString()));
log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.id, e.toString()));
this.searchSystem.pushResults(this, []);
}
},
@ -175,6 +255,7 @@ const RemoteSearchProvider = new Lang.Class({
metas[i][prop] = metas[i][prop].deep_unpack();
resultMetas.push({ id: metas[i]['id'],
name: metas[i]['name'],
description: metas[i]['description'],
createIcon: Lang.bind(this,
this.createIcon, metas[i]) });
}
@ -185,18 +266,42 @@ const RemoteSearchProvider = new Lang.Class({
this._cancellable.cancel();
this._cancellable.reset();
try {
this._proxy.GetResultMetasRemote(ids,
Lang.bind(this, this._getResultMetasFinished, callback),
this._cancellable);
this.proxy.GetResultMetasRemote(ids,
Lang.bind(this, this._getResultMetasFinished, callback),
this._cancellable);
} catch(e) {
log('Error calling GetResultMetas for provider %s: %s'.format(this.title, e.toString()));
log('Error calling GetResultMetas for provider %s: %s'.format(this.id, e.toString()));
callback([]);
}
},
activateResult: function(id) {
this._proxy.ActivateResultRemote(id);
this.proxy.ActivateResultRemote(id);
},
launchSearch: function(terms) {
// the provider is not compatible with the new version of the interface, launch
// the app itself but warn so we can catch the error in logs
log('Search provider ' + this.appInfo.get_id() + ' does not implement LaunchSearch');
this.appInfo.launch([], global.create_app_launch_context());
}
});
const RemoteSearchProvider2 = new Lang.Class({
Name: 'RemoteSearchProvider2',
Extends: RemoteSearchProvider,
_init: function(appInfo, dbusName, dbusPath) {
this.parent(appInfo, dbusName, dbusPath, SearchProvider2Proxy);
this.canLaunchSearch = true;
},
activateResult: function(id, terms) {
this.proxy.ActivateResultRemote(id, terms, global.get_current_time());
},
launchSearch: function(terms) {
this.proxy.LaunchSearchRemote(terms, global.get_current_time());
}
});

File diff suppressed because it is too large Load Diff

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

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

View File

@ -1,183 +1,25 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Signals = imports.signals;
const Shell = imports.gi.Shell;
const Util = imports.misc.util;
const FileUtils = imports.misc.fileUtils;
const Main = imports.ui.main;
// Not currently referenced by the search API, but
// this enumeration can be useful for provider
// implementations.
const MatchType = {
NONE: 0,
SUBSTRING: 1,
PREFIX: 2
};
const SearchResultDisplay = new Lang.Class({
Name: 'SearchResultDisplay',
_init: function(provider) {
this.provider = provider;
this.actor = null;
},
/**
* renderResults:
* @results: List of identifier strings
* @terms: List of search term strings
*
* Display the given search matches which resulted
* from the given terms. It's expected that not
* all results will fit in the space for the container
* actor; in this case, show as many as makes sense
* for your result type.
*
* The terms are useful for search match highlighting.
*/
renderResults: function(results, terms) {
throw new Error('Not implemented');
},
/**
* clear:
* Remove all results from this display.
*/
clear: function() {
this.actor.destroy_all_children();
},
/**
* getVisibleResultCount:
*
* Returns: The number of actors visible.
*/
getVisibleResultCount: function() {
throw new Error('Not implemented');
},
});
/**
* SearchProvider:
*
* Subclass this object to add a new result type
* to the search system, then call registerProvider()
* in SearchSystem with an instance.
* Search is asynchronous and uses the
* getInitialResultSet()/getSubsearchResultSet() methods.
*/
const SearchProvider = new Lang.Class({
Name: 'SearchProvider',
_init: function(title) {
this.title = title;
this.searchSystem = null;
},
/**
* getInitialResultSet:
* @terms: Array of search terms, treated as logical AND
*
* Called when the user first begins a search (most likely
* therefore a single term of length one or two), or when
* a new term is added.
*
* Should "return" an array of result identifier strings representing
* items which match the given search terms. This
* is expected to be a substring match on the metadata for a given
* item. Ordering of returned results is up to the discretion of the provider,
* but you should follow these heruistics:
*
* * Put items where the term matches multiple criteria (e.g. name and
* description) before single matches
* * Put items which match on a prefix before non-prefix substring matches
*
* We say "return" above, but in order to make the query asynchronous, use
* this.searchSystem.pushResults();. The return value should be ignored.
*
* This function should be fast; do not perform unindexed full-text searches
* or network queries.
*/
getInitialResultSet: function(terms) {
throw new Error('Not implemented');
},
/**
* getSubsearchResultSet:
* @previousResults: Array of item identifiers
* @newTerms: Updated search terms
*
* Called when a search is performed which is a "subsearch" of
* the previous search; i.e. when every search term has exactly
* one corresponding term in the previous search which is a prefix
* of the new term.
*
* This allows search providers to only search through the previous
* result set, rather than possibly performing a full re-query.
*
* Similar to getInitialResultSet, the return value for this will
* be ignored; use this.searchSystem.pushResults();.
*/
getSubsearchResultSet: function(previousResults, newTerms) {
throw new Error('Not implemented');
},
/**
* getResultMetas:
* @ids: Result identifier strings
*
* Call callback with array of objects with 'id', 'name', (both strings) and
* 'createIcon' (function(size) returning a Clutter.Texture) properties
* with the same number of members as @ids
*/
getResultMetas: function(ids, callback) {
throw new Error('Not implemented');
},
/**
* createResultActor:
* @resultMeta: Object with result metadata
* @terms: Array of search terms, should be used for highlighting
*
* Search providers may optionally override this to render a
* particular serch result in a custom fashion. The default
* implementation will show the icon next to the name.
*
* The actor should be an instance of St.Widget, with the style class
* 'search-result-content'.
*/
createResultActor: function(resultMeta, terms) {
return null;
},
/**
* activateResult:
* @id: Result identifier string
*
* Called when the user chooses a given result.
*/
activateResult: function(id) {
throw new Error('Not implemented');
}
});
Signals.addSignalMethods(SearchProvider.prototype);
const SEARCH_PROVIDERS_SCHEMA = 'org.gnome.desktop.search-providers';
const SearchSystem = new Lang.Class({
Name: 'SearchSystem',
_init: function() {
this._providers = [];
this._remoteProviders = [];
this.reset();
},
registerProvider: function (provider) {
provider.searchSystem = this;
this._providers.push(provider);
if (provider.isRemoteProvider)
this._remoteProviders.push(provider);
},
unregisterProvider: function (provider) {
@ -186,12 +28,20 @@ const SearchSystem = new Lang.Class({
return;
provider.searchSystem = null;
this._providers.splice(index, 1);
let remoteIndex = this._remoteProviders.indexOf(provider);
if (remoteIndex != -1)
this._remoteProviders.splice(index, 1);
},
getProviders: function() {
return this._providers;
},
getRemoteProviders: function() {
return this._remoteProviders;
},
getTerms: function() {
return this._previousTerms;
},
@ -210,28 +60,18 @@ const SearchSystem = new Lang.Class({
this.emit('search-updated', this._previousResults[i]);
},
updateSearch: function(searchString) {
searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
if (searchString == '')
return;
let terms = searchString.split(/\s+/);
this.updateSearchResults(terms);
},
updateSearchResults: function(terms) {
if (!terms)
return;
let isSubSearch = terms.length == this._previousTerms.length;
if (isSubSearch) {
for (let i = 0; i < terms.length; i++) {
if (terms[i].indexOf(this._previousTerms[i]) != 0) {
isSubSearch = false;
break;
}
}
}
let searchString = terms.join(' ');
let previousSearchString = this._previousTerms.join(' ');
if (searchString == previousSearchString)
return;
let isSubSearch = false;
if (this._previousTerms.length > 0)
isSubSearch = searchString.indexOf(previousSearchString) == 0;
let previousResultsArr = this._previousResults;
@ -246,7 +86,7 @@ const SearchSystem = new Lang.Class({
results.push([provider, []]);
provider.getSubsearchResultSet(previousResults, terms);
} catch (error) {
log('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
log('A ' + error.name + ' has occured in ' + provider.id + ': ' + error.message);
}
}
} else {
@ -256,10 +96,10 @@ const SearchSystem = new Lang.Class({
results.push([provider, []]);
provider.getInitialResultSet(terms);
} catch (error) {
log('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
log('A ' + error.name + ' has occured in ' + provider.id + ': ' + error.message);
}
}
}
},
}
});
Signals.addSignalMethods(SearchSystem.prototype);

View File

@ -11,10 +11,31 @@ const DND = imports.ui.dnd;
const IconGrid = imports.ui.iconGrid;
const Main = imports.ui.main;
const Overview = imports.ui.overview;
const Separator = imports.ui.separator;
const Search = imports.ui.search;
const MAX_SEARCH_RESULTS_ROWS = 1;
const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
const MaxWidthBin = new Lang.Class({
Name: 'MaxWidthBin',
Extends: St.Bin,
vfunc_allocate: function(box, flags) {
let themeNode = this.get_theme_node();
let maxWidth = themeNode.get_max_width();
let availWidth = box.x2 - box.x1;
let adjustedBox = box;
if (availWidth > maxWidth) {
let excessWidth = availWidth - maxWidth;
adjustedBox.x1 += Math.floor(excessWidth / 2);
adjustedBox.x2 -= Math.floor(excessWidth / 2);
}
this.parent(adjustedBox, flags);
}
});
const SearchResult = new Lang.Class({
Name: 'SearchResult',
@ -22,33 +43,102 @@ const SearchResult = new Lang.Class({
_init: function(provider, metaInfo, terms) {
this.provider = provider;
this.metaInfo = metaInfo;
this.actor = new St.Button({ style_class: 'search-result',
reactive: true,
this.terms = terms;
this.actor = new St.Button({ reactive: true,
can_focus: true,
track_hover: true,
x_align: St.Align.START,
y_fill: true });
this.actor._delegate = this;
this._dragActorSource = null;
this.actor.connect('clicked', Lang.bind(this, this.activate));
},
activate: function() {
this.provider.activateResult(this.metaInfo.id, this.terms);
Main.overview.toggle();
},
setSelected: function(selected) {
if (selected)
this.actor.add_style_pseudo_class('selected');
else
this.actor.remove_style_pseudo_class('selected');
}
});
const ListSearchResult = new Lang.Class({
Name: 'ListSearchResult',
Extends: SearchResult,
ICON_SIZE: 64,
_init: function(provider, metaInfo, terms) {
this.parent(provider, metaInfo, terms);
this.actor.style_class = 'list-search-result';
this.actor.x_fill = true;
let content = new St.BoxLayout({ style_class: 'list-search-result-content',
vertical: false });
this.actor.set_child(content);
// An icon for, or thumbnail of, content
let icon = this.metaInfo['createIcon'](this.ICON_SIZE);
if (icon) {
content.add(icon);
}
let details = new St.BoxLayout({ 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: 'list-search-result-title',
text: this.metaInfo['name'] })
details.add(title, { x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
if (this.metaInfo['description']) {
let description = new St.Label({ style_class: 'list-search-result-description' });
description.clutter_text.set_markup(this.metaInfo['description']);
details.add(description, { x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.END });
}
}
});
const GridSearchResult = new Lang.Class({
Name: 'GridSearchResult',
Extends: SearchResult,
_init: function(provider, metaInfo, terms) {
this.parent(provider, metaInfo, terms);
this.actor.style_class = 'grid-search-result';
let content = provider.createResultActor(metaInfo, terms);
let dragSource = null;
if (content == null) {
content = new St.Bin({ style_class: 'search-result-content',
reactive: true,
can_focus: true,
track_hover: true,
accessible_role: Atk.Role.PUSH_BUTTON });
content = new St.Bin();
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
{ createIcon: this.metaInfo['createIcon'] });
content.set_child(icon.actor);
this._dragActorSource = icon.icon;
content.label_actor = icon.label;
dragSource = icon.icon;
} else {
if (content._delegate && content._delegate.getDragActorSource)
this._dragActorSource = content._delegate.getDragActorSource();
dragSource = content._delegate.getDragActorSource();
}
this._content = content;
this.actor.set_child(content);
this.actor.connect('clicked', Lang.bind(this, this._onResultClicked));
this.actor.set_child(content);
let draggable = DND.makeDraggable(this.actor);
draggable.connect('drag-begin',
@ -63,32 +153,18 @@ const SearchResult = new Lang.Class({
Lang.bind(this, function() {
Main.overview.endItemDrag(this);
}));
},
setSelected: function(selected) {
if (selected)
this._content.add_style_pseudo_class('selected');
else
this._content.remove_style_pseudo_class('selected');
},
activate: function() {
this.provider.activateResult(this.metaInfo.id);
Main.overview.toggle();
},
_onResultClicked: function(actor) {
this.activate();
if (!dragSource)
// not exactly right, but alignment problems are hard to notice
dragSource = content;
this._dragActorSource = dragSource;
},
getDragActorSource: function() {
if (this._dragActorSource)
return this._dragActorSource;
// not exactly right, but alignment problems are hard to notice
return this._content;
return this._dragActorSource;
},
getDragActor: function(stageX, stageY) {
getDragActor: function() {
return this.metaInfo['createIcon'](Main.overview.dashIconSize);
},
@ -96,51 +172,52 @@ const SearchResult = new Lang.Class({
if (this.provider.dragActivateResult)
this.provider.dragActivateResult(this.metaInfo.id, params);
else
this.provider.activateResult(this.metaInfo.id, params);
this.provider.activateResult(this.metaInfo.id, this.terms);
}
});
const ListSearchResults = new Lang.Class({
Name: 'ListSearchResults',
const GridSearchResults = new Lang.Class({
Name: 'GridSearchResults',
Extends: Search.SearchResultDisplay,
_init: function(provider) {
this.provider = provider;
_init: function(provider, grid) {
this.parent(provider);
this._grid = grid || new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START });
this.actor = new St.Bin({ x_align: St.Align.START });
this.actor.set_child(this._grid.actor);
this._width = 0;
this.actor.connect('notify::width', Lang.bind(this, function() {
this._width = this.actor.width;
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.actor = new St.BoxLayout({ style_class: 'search-section-content' });
this.providerIcon = new ProviderIcon(provider);
this.providerIcon.connect('clicked', Lang.bind(this,
function() {
provider.launchSearch(this._terms);
Main.overview.toggle();
}));
}));
this.actor.add(this.providerIcon, { x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
this._content = new St.BoxLayout({ style_class: 'list-search-results',
vertical: true });
this.actor.add(this._content, { expand: true });
this._notDisplayedResult = [];
this._terms = [];
this._pendingClear = false;
},
getResultsForDisplay: function() {
let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount();
let canDisplay = this._grid.childrenInRow(this._width) * this._grid.getRowLimit()
- alreadyVisible;
let alreadyVisible = this._pendingClear ? 0 : this.getVisibleResultCount();
let canDisplay = MAX_LIST_SEARCH_RESULTS_ROWS - alreadyVisible;
let numResults = Math.min(this._notDisplayedResult.length, canDisplay);
return this._notDisplayedResult.splice(0, numResults);
let newResults = this._notDisplayedResult.splice(0, canDisplay);
return newResults;
},
getVisibleResultCount: function() {
return this._grid.visibleItemsCount();
return this._content.get_n_children();
},
hasMoreResults: function() {
return this._notDisplayedResult.length > 0;
},
setResults: function(results, terms) {
@ -152,7 +229,68 @@ 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 ListSearchResult(this.provider, metas[i], this._terms);
this._content.add_actor(display.actor);
}
},
clear: function () {
this._content.destroy_all_children();
this._pendingClear = false;
},
getFirstResult: function() {
if (this.getVisibleResultCount() > 0)
return this._content.get_child_at_index(0)._delegate;
else
return null;
}
});
const GridSearchResults = new Lang.Class({
Name: 'GridSearchResults',
_init: function(provider) {
this.provider = provider;
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START });
this.actor = new St.Bin({ x_align: St.Align.MIDDLE });
this.actor.set_child(this._grid.actor);
this._notDisplayedResult = [];
this._terms = [];
this._pendingClear = false;
},
getResultsForDisplay: function() {
let alreadyVisible = this._pendingClear ? 0 : this._grid.visibleItemsCount();
let canDisplay = this._grid.childrenInRow(this.actor.width) * this._grid.getRowLimit()
- alreadyVisible;
let newResults = this._notDisplayedResult.splice(0, canDisplay);
return newResults;
},
getVisibleResultCount: function() {
return this._grid.visibleItemsCount();
},
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 GridSearchResult(this.provider, metas[i], this._terms);
this._grid.addItem(display.actor);
}
},
@ -182,27 +320,29 @@ const SearchResults = new Lang.Class({
this._content = new St.BoxLayout({ name: 'searchResultsContent',
vertical: true });
this._contentBin = new MaxWidthBin({ name: 'searchResultsBin',
x_fill: true,
y_fill: true,
child: this._content });
let scrollView = new St.ScrollView({ x_fill: true,
y_fill: false,
style_class: 'vfade' });
scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
scrollView.add_actor(this._content);
let scrollChild = new St.BoxLayout();
scrollChild.add(this._contentBin, { expand: true });
this.actor.add(scrollView, { x_fill: true,
y_fill: true,
expand: true,
x_align: St.Align.START,
y_align: St.Align.START });
this.actor.connect('notify::mapped', Lang.bind(this,
function() {
if (!this.actor.mapped)
return;
this._scrollView = new St.ScrollView({ x_fill: true,
y_fill: false,
overlay_scrollbars: true,
style_class: 'search-display vfade' });
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
this._scrollView.add_actor(scrollChild);
let action = new Clutter.PanAction({ interpolate: true });
action.connect('pan', Lang.bind(this, this._onPan));
this._scrollView.add_action(action);
let adjustment = scrollView.vscroll.adjustment;
let direction = Overview.SwipeScrollDirection.VERTICAL;
Main.overview.setScrollAdjustment(adjustment, direction);
}));
this.actor.add(this._scrollView, { x_fill: true,
y_fill: true,
expand: true,
x_align: St.Align.START,
y_align: St.Align.START });
this._statusText = new St.Label({ style_class: 'search-statustext' });
this._statusBin = new St.Bin({ x_align: St.Align.MIDDLE,
@ -219,22 +359,37 @@ const SearchResults = new Lang.Class({
this._defaultResult = null;
},
_onPan: function(action) {
let [dist, dx, dy] = action.get_motion_delta(0);
let adjustment = this._scrollView.vscroll.adjustment;
adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
return false;
},
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, { x_fill: false, x_align: St.Align.START });
let providerIcon = null;
let resultDisplay = null;
let resultDisplayBin = new St.Bin({ style_class: 'search-section-results',
if (provider.appInfo) {
resultDisplay = new ListSearchResults(provider);
providerIcon = resultDisplay.providerIcon;
} else {
resultDisplay = new GridSearchResults(provider);
}
let resultDisplayBin = new St.Bin({ child: resultDisplay.actor,
x_fill: true,
y_fill: true });
providerBox.add(resultDisplayBin, { expand: true });
let resultDisplay = new GridSearchResults(provider);
resultDisplayBin.set_child(resultDisplay.actor);
let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
providerBox.add(separator.actor);
this._providerMeta.push({ provider: provider,
actor: providerBox,
icon: providerIcon,
resultDisplay: resultDisplay });
this._content.add(providerBox);
},
@ -268,18 +423,15 @@ const SearchResults = new Lang.Class({
this._searchSystem.reset();
this._statusBin.hide();
this._clearDisplay();
this._defaultResult = null;
},
startingSearch: function() {
this.reset();
this._statusText.set_text(_("Searching..."));
this._statusText.set_text(_("Searching"));
this._statusBin.show();
},
doSearch: function (searchString) {
this._searchSystem.updateSearch(searchString);
},
_metaForProvider: function(provider) {
return this._providerMeta[this._providers.indexOf(provider)];
},
@ -341,6 +493,11 @@ 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.canLaunchSearch;
provider.getResultMetas(results, Lang.bind(this, function(metas) {
this._clearDisplayForProvider(provider);
meta.actor.show();
@ -386,10 +543,37 @@ const SearchResults = new Lang.Class({
let from = this._defaultResult ? this._defaultResult.actor : null;
this.actor.navigate_focus(from, direction, false);
if (this._defaultResult) {
// The default result appears focused, so navigate directly to the
// next result.
this.actor.navigate_focus(global.stage.key_focus, direction, false);
}
}
});
const ProviderIcon = new Lang.Class({
Name: 'ProviderIcon',
Extends: St.Button,
PROVIDER_ICON_SIZE: 48,
_init: function(provider) {
this.provider = provider;
this.parent({ style_class: 'search-provider-icon',
reactive: true,
can_focus: true,
track_hover: true });
this._content = new St.Widget({ layout_manager: new Clutter.BinLayout() });
this.set_child(this._content);
let rtl = (this.get_text_direction() == Clutter.TextDirection.RTL);
this.moreIcon = new St.Widget({ style_class: 'search-provider-icon-more',
visible: false,
x_align: rtl ? Clutter.ActorAlign.START : Clutter.ActorAlign.END,
y_align: Clutter.ActorAlign.END,
x_expand: true,
y_expand: true });
let icon = new St.Icon({ icon_size: this.PROVIDER_ICON_SIZE,
gicon: provider.appInfo.get_icon() });
this._content.add_actor(icon);
this._content.add_actor(this.moreIcon);
}
});

35
js/ui/separator.js Normal file
View File

@ -0,0 +1,35 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Cairo = imports.cairo;
const Lang = imports.lang;
const St = imports.gi.St;
const HorizontalSeparator = new Lang.Class({
Name: 'HorizontalSeparator',
_init: function (params) {
this.actor = new St.DrawingArea(params);
this.actor.connect('repaint', Lang.bind(this, this._onRepaint));
},
_onRepaint: function(area) {
let cr = area.get_context();
let themeNode = area.get_theme_node();
let [width, height] = area.get_surface_size();
let margin = themeNode.get_length('-margin-horizontal');
let gradientHeight = themeNode.get_length('-gradient-height');
let startColor = themeNode.get_color('-gradient-start');
let endColor = themeNode.get_color('-gradient-end');
let gradientWidth = (width - margin * 2);
let gradientOffset = (height - gradientHeight) / 2;
let pattern = new Cairo.LinearGradient(margin, gradientOffset, width - margin, gradientOffset + gradientHeight);
pattern.addColorStopRGBA(0, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255);
pattern.addColorStopRGBA(0.5, endColor.red / 255, endColor.green / 255, endColor.blue / 255, endColor.alpha / 255);
pattern.addColorStopRGBA(1, startColor.red / 255, startColor.green / 255, startColor.blue / 255, startColor.alpha / 255);
cr.setSource(pattern);
cr.rectangle(margin, gradientOffset, gradientWidth, gradientHeight);
cr.fill();
cr.$dispose();
}
});

View File

@ -1,8 +1,12 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Signals = imports.signals;
const FileUtils = imports.misc.fileUtils;
const Main = imports.ui.main;
const Params = imports.misc.params;
@ -10,11 +14,13 @@ const DEFAULT_MODE = 'restrictive';
const _modes = {
'restrictive': {
parentMode: null,
stylesheetName: 'gnome-shell.css',
hasOverview: false,
showCalendarEvents: false,
allowSettings: false,
allowExtensions: false,
allowKeybindingsWhenModal: false,
enabledExtensions: [],
hasRunDialog: false,
hasWorkspaces: false,
hasWindows: false,
@ -29,10 +35,10 @@ const _modes = {
center: [],
right: []
},
panelStyle: null
},
'gdm': {
allowKeybindingsWhenModal: true,
hasNotifications: true,
isGreeter: true,
isPrimary: true,
@ -41,9 +47,10 @@ const _modes = {
panel: {
left: ['logo'],
center: ['dateMenu'],
right: ['a11y', 'display', 'keyboard',
right: ['a11yGreeter', 'display', 'keyboard',
'volume', 'battery', 'powerMenu']
}
},
panelStyle: 'login-screen'
},
'lock-screen': {
@ -56,6 +63,7 @@ const _modes = {
center: [],
right: ['lockScreen']
},
panelStyle: 'lock-screen'
},
'unlock-dialog': {
@ -67,6 +75,7 @@ const _modes = {
center: [],
right: ['a11y', 'keyboard', 'lockScreen']
},
panelStyle: 'unlock-screen'
},
'initial-setup': {
@ -75,7 +84,7 @@ const _modes = {
panel: {
left: [],
center: ['dateMenu'],
right: ['a11y', 'keyboard', 'volume']
right: ['a11yGreeter', 'keyboard', 'volume']
}
},
@ -102,11 +111,48 @@ const _modes = {
}
};
function _getModes(modesLoadedCallback) {
FileUtils.collectFromDatadirsAsync('modes',
{ processFile: _loadMode,
loadedCallback: modesLoadedCallback,
data: _modes });
}
function _loadMode(file, info, loadedData) {
let name = info.get_name();
let suffix = name.indexOf('.json');
let modeName = suffix == -1 ? name : name.slice(name, suffix);
if (loadedData.hasOwnProperty(modeName))
return;
let fileContent, success, tag, newMode;
try {
[success, fileContent, tag] = file.load_contents(null);
newMode = JSON.parse(fileContent);
} catch(e) {
return;
}
loadedData[modeName] = {};
let propBlacklist = ['unlockDialog'];
for (let prop in loadedData[DEFAULT_MODE]) {
if (newMode[prop] !== undefined &&
propBlacklist.indexOf(prop) == -1)
loadedData[modeName][prop]= newMode[prop];
}
loadedData[modeName]['isPrimary'] = true;
}
function listModes() {
let modes = Object.getOwnPropertyNames(_modes);
for (let i = 0; i < modes.length; i++)
if (_modes[modes[i]].isPrimary)
print(modes[i]);
_getModes(function(modes) {
let names = Object.getOwnPropertyNames(modes);
for (let i = 0; i < names.length; i++)
if (_modes[names[i]].isPrimary)
print(names[i]);
Mainloop.quit('listModes');
});
Mainloop.run('listModes');
}
const SessionMode = new Lang.Class({
@ -114,10 +160,18 @@ const SessionMode = new Lang.Class({
_init: function() {
global.connect('notify::session-mode', Lang.bind(this, this._sync));
let mode = _modes[global.session_mode].isPrimary ? global.session_mode
: 'user';
this._modeStack = [mode];
this._modes = _modes;
this._modeStack = [DEFAULT_MODE];
this._sync();
_getModes(Lang.bind(this, function(modes) {
this._modes = modes;
let primary = modes[global.session_mode] &&
modes[global.session_mode].isPrimary;
let mode = primary ? global.session_mode : 'user';
this._modeStack = [mode];
this._sync();
}));
},
pushMode: function(mode) {
@ -144,8 +198,14 @@ const SessionMode = new Lang.Class({
},
_sync: function() {
let params = _modes[this.currentMode];
params = Params.parse(params, _modes[DEFAULT_MODE]);
let params = this._modes[this.currentMode];
let defaults;
if (params.parentMode)
defaults = Params.parse(this._modes[params.parentMode],
this._modes[DEFAULT_MODE]);
else
defaults = this._modes[DEFAULT_MODE];
params = Params.parse(params, defaults);
// A simplified version of Lang.copyProperties, handles
// undefined as a special case for "no change / inherit from previous mode"

View File

@ -1,16 +1,18 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Config = imports.misc.config;
const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionDownloader = imports.ui.extensionDownloader;
const ExtensionUtils = imports.misc.extensionUtils;
const Flashspot = imports.ui.flashspot;
const Hash = imports.misc.hash;
const Main = imports.ui.main;
const Screenshot = imports.ui.screenshot;
const GnomeShellIface = <interface name="org.gnome.Shell">
<method name="Eval">
@ -18,34 +20,27 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
<arg type="b" direction="out" name="success" />
<arg type="s" direction="out" name="result" />
</method>
<method name="ScreenshotArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<method name="ShowOSD">
<arg type="a{sv}" direction="in" name="params"/>
</method>
<method name="GrabAccelerator">
<arg type="s" direction="in" name="accelerator"/>
<arg type="u" direction="in" name="flags"/>
<arg type="u" direction="out" name="action"/>
</method>
<method name="GrabAccelerators">
<arg type="a(su)" direction="in" name="accelerators"/>
<arg type="au" direction="out" name="actions"/>
</method>
<method name="UngrabAccelerator">
<arg type="u" direction="in" name="action"/>
<arg type="b" direction="out" name="success"/>
</method>
<method name="ScreenshotWindow">
<arg type="b" direction="in" name="include_frame"/>
<arg type="b" direction="in" name="include_cursor"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
</method>
<method name="Screenshot">
<arg type="b" direction="in" name="include_cursor"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
</method>
<method name="FlashArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
</method>
<signal name="AcceleratorActivated">
<arg name="action" type="u" />
<arg name="deviceid" type="u" />
</signal>
<property name="Mode" type="s" access="read" />
<property name="OverviewActive" type="b" access="readwrite" />
<property name="ShellVersion" type="s" access="read" />
</interface>;
@ -74,7 +69,16 @@ const GnomeShell = new Lang.Class({
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
this._extensionsSerivce = new GnomeShellExtensions();
this._extensionsService = new GnomeShellExtensions();
this._screenshotService = new Screenshot.ScreenshotService();
this._grabbedAccelerators = new Hash.Map();
this._grabbers = new Hash.Map();
global.display.connect('accelerator-activated', Lang.bind(this,
function(display, action, deviceid) {
this._emitAcceleratorActivated(action, deviceid);
}));
},
/**
@ -110,81 +114,103 @@ const GnomeShell = new Lang.Class({
return [success, returnValue];
},
_onScreenshotComplete: function(obj, result, area, flash, invocation) {
if (flash) {
let flashspot = new Flashspot.Flashspot(area);
flashspot.fire();
ShowOSD: function(params) {
for (let param in params)
params[param] = params[param].deep_unpack();
let icon = null;
if (params['icon'])
icon = Gio.Icon.new_for_string(params['icon']);
Main.osdWindow.setIcon(icon);
Main.osdWindow.setLabel(params['label']);
Main.osdWindow.setLevel(params['level']);
Main.osdWindow.show();
},
GrabAcceleratorAsync: function(params, invocation) {
let [accel, flags] = params;
let sender = invocation.get_sender();
let bindingAction = this._grabAcceleratorForSender(accel, flags, sender);
return invocation.return_value(GLib.Variant.new('(u)', [bindingAction]));
},
GrabAcceleratorsAsync: function(params, invocation) {
let [accels] = params;
let sender = invocation.get_sender();
let bindingActions = [];
for (let i = 0; i < accels.length; i++) {
let [accel, flags] = accels[i];
bindingActions.push(this._grabAcceleratorForSender(accel, flags, sender));
}
return invocation.return_value(GLib.Variant.new('(au)', [bindingActions]));
},
UngrabAcceleratorAsync: function(params, invocation) {
let [action] = params;
let grabbedBy = this._grabbedAccelerators.get(action);
if (invocation.get_sender() != grabbedBy)
return invocation.return_value(GLib.Variant.new('(b)', [false]));
let ungrabSucceeded = global.display.ungrab_accelerator(action);
if (ungrabSucceeded)
this._grabbedAccelerators.delete(action);
return invocation.return_value(GLib.Variant.new('(b)', [ungrabSucceeded]));
},
_emitAcceleratorActivated: function(action, deviceid) {
let destination = this._grabbedAccelerators.get(action);
if (!destination)
return;
let connection = this._dbusImpl.get_connection();
let info = this._dbusImpl.get_info();
connection.emit_signal(destination,
this._dbusImpl.get_object_path(),
info ? info.name : null,
'AcceleratorActivated',
GLib.Variant.new('(uu)', [action, deviceid]));
},
_grabAcceleratorForSender: function(accelerator, flags, sender) {
let bindingAction = global.display.grab_accelerator(accelerator);
if (bindingAction == Meta.KeyBindingAction.NONE)
return Meta.KeyBindingAction.NONE;
let bindingName = Meta.external_binding_name_for_action(bindingAction);
Main.wm.allowKeybinding(bindingName, flags);
this._grabbedAccelerators.set(bindingAction, sender);
if (!this._grabbers.has(sender)) {
let id = Gio.bus_watch_name(Gio.BusType.SESSION, sender, 0, null,
Lang.bind(this, this._onGrabberBusNameVanished));
this._grabbers.set(sender, id);
}
let retval = GLib.Variant.new('(b)', [result]);
invocation.return_value(retval);
return bindingAction;
},
/**
* ScreenshotArea:
* @x: The X coordinate of the area
* @y: The Y coordinate of the area
* @width: The width of the area
* @height: The height of the area
* @flash: Whether to flash the area or not
* @filename: The filename for the screenshot
*
* Takes a screenshot of the passed in area and saves it
* in @filename as png image, it returns a boolean
* indicating whether the operation was successful or not.
*
*/
ScreenshotAreaAsync : function (params, invocation) {
let [x, y, width, height, flash, filename, callback] = params;
let screenshot = new Shell.Screenshot();
screenshot.screenshot_area (x, y, width, height, filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
_ungrabAccelerator: function(action) {
let ungrabSucceeded = global.display.ungrab_accelerator(action);
if (ungrabSucceeded)
this._grabbedAccelerators.delete(action);
},
/**
* ScreenshotWindow:
* @include_frame: Whether to include the frame or not
* @include_cursor: Whether to include the cursor image or not
* @flash: Whether to flash the window area or not
* @filename: The filename for the screenshot
*
* Takes a screenshot of the focused window (optionally omitting the frame)
* and saves it in @filename as png image, it returns a boolean
* indicating whether the operation was successful or not.
*
*/
ScreenshotWindowAsync : function (params, invocation) {
let [include_frame, include_cursor, flash, filename] = params;
let screenshot = new Shell.Screenshot();
screenshot.screenshot_window (include_frame, include_cursor, filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
_onGrabberBusNameVanished: function(connection, name) {
let grabs = this._grabbedAccelerators.items();
for (let i = 0; i < grabs.length; i++) {
let [action, sender] = grabs[i];
if (sender == name)
this._ungrabAccelerator(action);
}
Gio.bus_unwatch_name(this._grabbers.get(name));
this._grabbers.delete(name);
},
/**
* Screenshot:
* @filename: The filename for the screenshot
* @include_cursor: Whether to include the cursor image or not
* @flash: Whether to flash the screen or not
*
* Takes a screenshot of the whole screen and saves it
* in @filename as png image, it returns a boolean
* indicating whether the operation was successful or not.
*
*/
ScreenshotAsync : function (params, invocation) {
let [include_cursor, flash, filename] = params;
let screenshot = new Shell.Screenshot();
screenshot.screenshot(include_cursor, filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
},
FlashArea: function(x, y, width, height) {
let flashspot = new Flashspot.Flashspot({ x : x, y : y, width: width, height: height});
flashspot.fire();
},
Mode: global.session_mode,
get OverviewActive() {
return Main.overview.visible;
@ -347,8 +373,8 @@ const ScreenSaverDBus = new Lang.Class({
this.parent();
this._screenShield = screenShield;
screenShield.connect('lock-status-changed', Lang.bind(this, function(shield) {
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.locked]));
screenShield.connect('active-changed', Lang.bind(this, function(shield) {
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.active]));
}));
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this);
@ -369,13 +395,13 @@ const ScreenSaverDBus = new Lang.Class({
SetActive: function(active) {
if (active)
this._screenShield.lock(true);
this._screenShield.activate(true);
else
this._screenShield.unlock();
this._screenShield.unlock(false);
},
GetActive: function() {
return this._screenShield.locked;
return this._screenShield.active;
},
GetActiveTime: function() {

View File

@ -1,8 +1,11 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const St = imports.gi.St;
const BoxPointer = imports.ui.boxpointer;
const Main = imports.ui.main;
const Params = imports.misc.params;
const PopupMenu = imports.ui.popupMenu;
@ -65,32 +68,33 @@ const EntryMenu = new Lang.Class({
}
},
open: function() {
open: function(animate) {
this._updatePasteItem();
this._updateCopyItem();
if (this._passwordItem)
this._updatePasswordItem();
this.parent(animate);
this._entry.add_style_pseudo_class('focus');
let direction = Gtk.DirectionType.TAB_FORWARD;
if (!this.actor.navigate_focus(null, direction, false))
this.actor.grab_key_focus();
this.parent();
this._entry.add_style_pseudo_class('focus');
},
close: function() {
close: function(animate) {
this._entry.grab_key_focus();
this.parent();
this.parent(animate);
},
_updateCopyItem: function() {
let selection = this._entry.clutter_text.get_selection();
this._copyItem.setSensitive(selection && selection != '');
this._copyItem.setSensitive(!this._entry.clutter_text.password_char &&
selection && selection != '');
},
_updatePasteItem: function() {
this._clipboard.get_text(Lang.bind(this,
this._clipboard.get_text(St.ClipboardType.CLIPBOARD, Lang.bind(this,
function(clipboard, text) {
this._pasteItem.setSensitive(text && text != '');
}));
@ -106,11 +110,11 @@ const EntryMenu = new Lang.Class({
_onCopyActivated: function() {
let selection = this._entry.clutter_text.get_selection();
this._clipboard.set_text(selection);
this._clipboard.set_text(St.ClipboardType.CLIPBOARD, selection);
},
_onPasteActivated: function() {
this._clipboard.get_text(Lang.bind(this,
this._clipboard.get_text(St.ClipboardType.CLIPBOARD, Lang.bind(this,
function(clipboard, text) {
if (!text)
return;
@ -134,12 +138,12 @@ function _setMenuAlignment(entry, stageX) {
function _onButtonPressEvent(actor, event, entry) {
if (entry.menu.isOpen) {
entry.menu.close();
entry.menu.close(BoxPointer.PopupAnimation.FULL);
return true;
} else if (event.get_button() == 3) {
let [stageX, stageY] = event.get_coords();
_setMenuAlignment(entry, stageX);
entry.menu.open();
entry.menu.open(BoxPointer.PopupAnimation.FULL);
return true;
}
return false;
@ -149,7 +153,7 @@ function _onPopup(actor, entry) {
let [success, textX, textY, lineHeight] = entry.clutter_text.position_to_coords(-1);
if (success)
entry.menu.setSourceAlignment(textX / entry.width);
entry.menu.open();
entry.menu.open(BoxPointer.PopupAnimation.FULL);
};
function addContextMenu(entry, params) {

View File

@ -19,6 +19,8 @@ const ShellEntry = imports.ui.shellEntry;
const LIST_ITEM_ICON_SIZE = 48;
const REMEMBER_MOUNT_PASSWORD_KEY = 'remember-mount-password';
/* ------ Common Utils ------- */
function _setLabelText(label, text) {
if (text) {
@ -387,7 +389,8 @@ const ShellMountPasswordDialog = new Lang.Class({
if (flags & Gio.AskPasswordFlags.SAVING_SUPPORTED) {
this._rememberChoice = new CheckBox.CheckBox();
this._rememberChoice.getLabelActor().text = _("Remember Password");
this._rememberChoice.actor.checked = true;
this._rememberChoice.actor.checked =
global.settings.get_boolean(REMEMBER_MOUNT_PASSWORD_KEY);
this._messageBox.add(this._rememberChoice.actor);
} else {
this._rememberChoice = null;
@ -419,6 +422,8 @@ const ShellMountPasswordDialog = new Lang.Class({
},
_onEntryActivate: function() {
global.settings.set_boolean(REMEMBER_MOUNT_PASSWORD_KEY,
this._rememberChoice && this._rememberChoice.actor.checked);
this.emit('response', 1,
this._passwordEntry.get_text(),
this._rememberChoice &&

View File

@ -2,6 +2,7 @@
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
@ -14,12 +15,7 @@ const KEY_MOUSE_KEYS_ENABLED = 'mousekeys-enable';
const APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
const DPI_LOW_REASONABLE_VALUE = 50;
const DPI_HIGH_REASONABLE_VALUE = 500;
const DPI_FACTOR_LARGE = 1.25;
const DPI_FACTOR_LARGER = 1.5;
const DPI_FACTOR_LARGEST = 2.0;
const WM_SCHEMA = 'org.gnome.desktop.wm.preferences';
const KEY_VISUAL_BELL = 'visual-bell';
@ -27,6 +23,7 @@ const KEY_VISUAL_BELL = 'visual-bell';
const DESKTOP_INTERFACE_SCHEMA = 'org.gnome.desktop.interface';
const KEY_GTK_THEME = 'gtk-theme';
const KEY_ICON_THEME = 'icon-theme';
const KEY_WM_THEME = 'theme';
const KEY_TEXT_SCALING_FACTOR = 'text-scaling-factor';
const HIGH_CONTRAST_THEME = 'HighContrast';
@ -73,6 +70,25 @@ const ATIndicator = new Lang.Class({
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Universal Access Settings"), 'gnome-universal-access-panel.desktop');
this._syncMenuVisibility();
},
_syncMenuVisibility: function() {
this._syncMenuVisibilityIdle = 0;
let items = this.menu._getMenuItems();
this.actor.visible = items.some(function(f) { return !!f.state; });
return false;
},
_queueSyncMenuVisibility: function() {
if (this._syncMenuVisibilityIdle)
return;
this._syncMenuVisbilityIdle = Mainloop.idle_add(Lang.bind(this, this._syncMenuVisibility));
},
_buildItemExtended: function(string, initial_value, writable, on_set) {
@ -94,47 +110,63 @@ const ATIndicator = new Lang.Class({
function(enabled) {
return settings.set_boolean(key, enabled);
});
settings.connect('changed::'+key, function() {
settings.connect('changed::'+key, Lang.bind(this, function() {
widget.setToggleState(settings.get_boolean(key));
});
this._queueSyncMenuVisibility();
}));
return widget;
},
_buildHCItem: function() {
let settings = new Gio.Settings({ schema: DESKTOP_INTERFACE_SCHEMA });
let gtkTheme = settings.get_string(KEY_GTK_THEME);
let iconTheme = settings.get_string(KEY_ICON_THEME);
let interfaceSettings = new Gio.Settings({ schema: DESKTOP_INTERFACE_SCHEMA });
let wmSettings = new Gio.Settings({ schema: WM_SCHEMA });
let gtkTheme = interfaceSettings.get_string(KEY_GTK_THEME);
let iconTheme = interfaceSettings.get_string(KEY_ICON_THEME);
let wmTheme = wmSettings.get_string(KEY_WM_THEME);
let hasHC = (gtkTheme == HIGH_CONTRAST_THEME);
let highContrast = this._buildItemExtended(
_("High Contrast"),
hasHC,
settings.is_writable(KEY_GTK_THEME) && settings.is_writable(KEY_ICON_THEME),
interfaceSettings.is_writable(KEY_GTK_THEME) &&
interfaceSettings.is_writable(KEY_ICON_THEME) &&
wmSettings.is_writable(KEY_WM_THEME),
function (enabled) {
if (enabled) {
settings.set_string(KEY_GTK_THEME, HIGH_CONTRAST_THEME);
settings.set_string(KEY_ICON_THEME, HIGH_CONTRAST_THEME);
interfaceSettings.set_string(KEY_GTK_THEME, HIGH_CONTRAST_THEME);
interfaceSettings.set_string(KEY_ICON_THEME, HIGH_CONTRAST_THEME);
wmSettings.set_string(KEY_WM_THEME, HIGH_CONTRAST_THEME);
} else if(!hasHC) {
settings.set_string(KEY_GTK_THEME, gtkTheme);
settings.set_string(KEY_ICON_THEME, iconTheme);
interfaceSettings.set_string(KEY_GTK_THEME, gtkTheme);
interfaceSettings.set_string(KEY_ICON_THEME, iconTheme);
wmSettings.set_string(KEY_WM_THEME, wmTheme);
} else {
settings.reset(KEY_GTK_THEME);
settings.reset(KEY_ICON_THEME);
interfaceSettings.reset(KEY_GTK_THEME);
interfaceSettings.reset(KEY_ICON_THEME);
wmSettings.reset(KEY_WM_THEME);
}
});
settings.connect('changed::' + KEY_GTK_THEME, function() {
let value = settings.get_string(KEY_GTK_THEME);
interfaceSettings.connect('changed::' + KEY_GTK_THEME, Lang.bind(this, function() {
let value = interfaceSettings.get_string(KEY_GTK_THEME);
if (value == HIGH_CONTRAST_THEME) {
highContrast.setToggleState(true);
} else {
highContrast.setToggleState(false);
gtkTheme = value;
}
});
settings.connect('changed::' + KEY_ICON_THEME, function() {
let value = settings.get_string(KEY_ICON_THEME);
this._queueSyncMenuVisibility();
}));
interfaceSettings.connect('changed::' + KEY_ICON_THEME, function() {
let value = interfaceSettings.get_string(KEY_ICON_THEME);
if (value != HIGH_CONTRAST_THEME)
iconTheme = value;
});
wmSettings.connect('changed::' + KEY_WM_THEME, function() {
let value = wmSettings.get_string(KEY_WM_THEME);
if (value != HIGH_CONTRAST_THEME)
wmTheme = value;
});
return highContrast;
},
@ -153,11 +185,22 @@ const ATIndicator = new Lang.Class({
else
settings.reset(KEY_TEXT_SCALING_FACTOR);
});
settings.connect('changed::' + KEY_TEXT_SCALING_FACTOR, function() {
settings.connect('changed::' + KEY_TEXT_SCALING_FACTOR, Lang.bind(this, function() {
let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR);
let active = (factor > 1.0);
widget.setToggleState(active);
});
this._queueSyncMenuVisibility();
}));
return widget;
}
});
const ATGreeterIndicator = new Lang.Class({
Name: 'ATGreeterIndicator',
Extends: ATIndicator,
// Override visibility handling to be always visible
_syncMenuVisibility: function() { },
_queueSyncMenuVisibility: function() { }
});

View File

@ -9,6 +9,7 @@ const St = imports.gi.St;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
@ -55,8 +56,8 @@ const Indicator = new Lang.Class({
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._fullMenuItems = [new PopupMenu.PopupSeparatorMenuItem(),
new PopupMenu.PopupMenuItem(_("Send Files to Device...")),
new PopupMenu.PopupMenuItem(_("Set up a New Device...")),
new PopupMenu.PopupMenuItem(_("Send Files to Device")),
new PopupMenu.PopupMenuItem(_("Set Up a New Device")),
new PopupMenu.PopupSeparatorMenuItem()];
this._hasDevices = false;
@ -80,7 +81,7 @@ const Indicator = new Lang.Class({
this._applet.connect('notify::show-full-menu', Lang.bind(this, this._updateFullMenu));
this._updateFullMenu();
this.menu.addSettingsAction(_("Bluetooth Settings"), 'bluetooth-properties.desktop');
this.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
@ -235,26 +236,10 @@ const Indicator = new Lang.Class({
}
if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_PUSH) {
item.menu.addAction(_("Send Files..."), Lang.bind(this, function() {
item.menu.addAction(_("Send Files"), Lang.bind(this, function() {
this._applet.send_to_address(device.bdaddr, device.alias);
}));
}
if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_FILE_TRANSFER) {
item.menu.addAction(_("Browse Files..."), Lang.bind(this, function(event) {
this._applet.browse_address(device.bdaddr, event.get_time(),
Lang.bind(this, function(applet, result) {
try {
applet.browse_address_finish(result);
} catch (e) {
this._ensureSource();
this._source.notify(new MessageTray.Notification(this._source,
_("Bluetooth"),
_("Error browsing device"),
{ body: _("The requested device cannot be browsed, error is '%s'").format(e) }));
}
}));
}));
}
switch (device.type) {
case GnomeBluetoothApplet.Type.KEYBOARD:
@ -302,6 +287,7 @@ const Indicator = new Lang.Class({
_ensureSource: function() {
if (!this._source) {
this._source = new MessageTray.Source(_("Bluetooth"), 'bluetooth-active');
this._source.policy = new NotificationDaemon.NotificationApplicationPolicy('gnome-bluetooth-panel');
Main.messageTray.add(this._source);
}
},
@ -369,6 +355,7 @@ const ConfirmNotification = new Lang.Class({
_init: function(source, applet, device_path, name, long_name, pin) {
this.parent(source,
_("Bluetooth"),
/* Translators: argument is the device short name */
_("Pairing confirmation for %s").format(name),
{ customContent: true });
this.setResident(true);
@ -378,6 +365,7 @@ const ConfirmNotification = new Lang.Class({
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
this.addBody(_("Please confirm whether the PIN '%06d' matches the one on the device.").format(pin));
/* Translators: this is the verb, not the noun */
this.addButton('matches', _("Matches"));
this.addButton('does-not-match', _("Does not match"));
@ -460,10 +448,5 @@ const PinNotification = new Lang.Class({
return this._entry.clutter_text.text.length == 6;
else
return true;
},
grabFocus: function(lockTray) {
this.parent(lockTray);
global.stage.set_key_focus(this._entry);
}
});

View File

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

View File

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

View File

@ -8,16 +8,26 @@ const NMClient = imports.gi.NMClient;
const Signals = imports.signals;
const St = imports.gi.St;
// Some of the new code depends on as-yet-unreleased NM
var NMGtk;
try {
NMGtk = imports.gi.NMGtk;
} catch(e) {
NMGtk = null;
}
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon;
const ModemManager = imports.misc.modemManager;
const Util = imports.misc.util;
const NMConnectionCategory = {
INVALID: 'invalid',
WIRED: 'wired',
VIRTUAL: 'virtual',
WIRELESS: 'wireless',
WWAN: 'wwan',
VPN: 'vpn'
@ -39,7 +49,7 @@ const NM80211ApFlags = NetworkManager['80211ApFlags'];
const NM80211ApSecurityFlags = NetworkManager['80211ApSecurityFlags'];
// number of wireless networks that should be visible
// (the remaining are placed into More...)
// (the remaining are placed into More)
const NUM_VISIBLE_NETWORKS = 5;
function macToArray(string) {
@ -139,46 +149,6 @@ const NMNetworkMenuItem = new Lang.Class({
}
});
const NMWiredSectionTitleMenuItem = new Lang.Class({
Name: 'NMWiredSectionTitleMenuItem',
Extends: PopupMenu.PopupSwitchMenuItem,
_init: function(label, params) {
params = params || { };
params.style_class = 'popup-subtitle-menu-item';
this.parent(label, false, params);
},
updateForDevice: function(device) {
if (device) {
this._device = device;
this.setStatus(device.getStatusLabel());
this.setToggleState(device.connected);
} else
this.setStatus('');
},
activate: function(event) {
this.parent(event);
if (!this._device) {
log('Section title activated when there is more than one device, should be non reactive');
return;
}
let newState = this._switch.state;
let ok;
if (newState)
ok = this._device.activate();
else
ok = this._device.deactivate();
if (!ok)
this._switch.setToggleState(false);
}
});
const NMWirelessSectionTitleMenuItem = new Lang.Class({
Name: 'NMWirelessSectionTitleMenuItem',
Extends: PopupMenu.PopupSwitchMenuItem,
@ -339,19 +309,16 @@ const NMDevice = new Lang.Class({
Extends: NMConnectionBased,
_init: function(client, device, connections) {
this.device = device;
this.device._delegate = this;
this._stateChangedId = this.device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
// protected
this._client = client;
this._setDevice(device);
this.parent(connections);
this._activeConnection = null;
this._activeConnectionItem = null;
this._autoConnectionItem = null;
this._overflowItem = null;
this.statusItem = new PopupMenu.PopupSwitchMenuItem(this._getDescription(), this.connected, { style_class: 'popup-subtitle-menu-item' });
this.statusItem = new PopupMenu.PopupSwitchMenuItem('', this.connected, { style_class: 'popup-subtitle-menu-item' });
this._statusChanged = this.statusItem.connect('toggled', Lang.bind(this, function(item, state) {
let ok;
if (state)
@ -370,23 +337,12 @@ const NMDevice = new Lang.Class({
},
destroy: function() {
if (this.device)
this.device._delegate = null;
this._setDevice(null);
if (this._stateChangedId) {
// Need to go through GObject.Object.prototype because
// nm_device_disconnect conflicts with g_signal_disconnect
GObject.Object.prototype.disconnect.call(this.device, this._stateChangedId);
this._stateChangedId = 0;
}
if (this._carrierChangedId) {
// see above for why this is needed
GObject.Object.prototype.disconnect.call(this.device, this._carrierChangedId);
this._carrierChangedId = 0;
}
if (this._firmwareChangedId) {
GObject.Object.prototype.disconnect.call(this.device, this._firmwareChangedId);
this._firmwareChangedId = 0;
if (this._deferredWorkId) {
// Just clear out, the actual removal is handled when the
// actor is destroyed
this._deferredWorkId = 0;
}
this._clearSection();
@ -395,6 +351,33 @@ const NMDevice = new Lang.Class({
this.section.destroy();
},
_setDevice: function(device) {
if (device) {
this.device = device;
this.device._delegate = this;
this._stateChangedId = this.device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
} else if (this.device) {
this.device._delegate = null;
if (this._stateChangedId) {
// Need to go through GObject.Object.prototype because
// nm_device_disconnect conflicts with g_signal_disconnect
GObject.Object.prototype.disconnect.call(this.device, this._stateChangedId);
this._stateChangedId = 0;
}
if (this._carrierChangedId) {
GObject.Object.prototype.disconnect.call(this.device, this._carrierChangedId);
this._carrierChangedId = 0;
}
if (this._firmwareChangedId) {
GObject.Object.prototype.disconnect.call(this.device, this._firmwareChangedId);
this._firmwareChangedId = 0;
}
this.device = null;
}
},
deactivate: function() {
this.device.disconnect(null);
return true;
@ -409,7 +392,7 @@ const NMDevice = new Lang.Class({
// Otherwise, if no connection is currently configured,
// try automatic configuration (or summon the config dialog)
if (this._connections.length == 1) {
this._client.activate_connection(this._connections[0].connection, this.device, null, null);
this._client.activate_connection(this._connections[0].connection, this.device || null, null, null);
return true;
} else if (this._connections.length == 0) {
return this._activateAutomaticConnection();
@ -429,7 +412,7 @@ const NMDevice = new Lang.Class({
},
get connected() {
return this.device.state == NetworkManager.DeviceState.ACTIVATED;
return this.device && this.device.state == NetworkManager.DeviceState.ACTIVATED;
},
clearActiveConnection: function(activeConnection) {
@ -449,7 +432,6 @@ const NMDevice = new Lang.Class({
this._activeConnection = activeConnection;
this._clearSection();
this._queueCreateSection();
},
@ -463,6 +445,9 @@ const NMDevice = new Lang.Class({
},
getStatusLabel: function() {
if (!this.device)
return null;
switch(this.device.state) {
case NetworkManager.DeviceState.DISCONNECTED:
case NetworkManager.DeviceState.ACTIVATED:
@ -512,14 +497,21 @@ const NMDevice = new Lang.Class({
}
},
syncDescription: function() {
if (this.device && this.device._description)
this.statusItem.label.text = this.device._description;
},
// protected
_createAutomaticConnection: function() {
throw new TypeError('Invoking pure virtual function NMDevice.createAutomaticConnection');
},
_queueCreateSection: function() {
this._clearSection();
Main.queueDeferredWork(this._deferredWorkId);
if (this._deferredWorkId) {
this._clearSection();
Main.queueDeferredWork(this._deferredWorkId);
}
},
_clearSection: function() {
@ -557,7 +549,7 @@ const NMDevice = new Lang.Class({
if (j + activeOffset >= NUM_VISIBLE_NETWORKS) {
if (!this._overflowItem) {
this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More..."));
this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More"));
this.section.addMenuItem(this._overflowItem);
}
this._overflowItem.menu.addMenuItem(obj.item);
@ -609,13 +601,16 @@ const NMDevice = new Lang.Class({
this.emit('network-lost');
}
if (newstate == NetworkManager.DeviceState.FAILED) {
/* Emit a notification if activation fails, but don't do it
if the reason is no secrets, as that indicates the user
cancelled the agent dialog */
if (newstate == NetworkManager.DeviceState.FAILED &&
reason != NetworkManager.DeviceStateReason.NO_SECRETS) {
this.emit('activation-failed', reason);
}
this._updateStatusItem();
this._clearSection();
this._queueCreateSection();
this.emit('state-changed');
},
@ -639,34 +634,14 @@ const NMDevice = new Lang.Class({
this.statusItem.setStatus(this.getStatusLabel());
this.emit('state-changed');
},
_getDescription: function() {
let dev_product = this.device.get_product();
let dev_vendor = this.device.get_vendor();
if (!dev_product || !dev_vendor)
return '';
let product = Util.fixupPCIDescription(dev_product);
let vendor = Util.fixupPCIDescription(dev_vendor);
let out = '';
// Another quick hack; if all of the fixed up vendor string
// is found in product, ignore the vendor.
if (product.indexOf(vendor) == -1)
out += vendor + ' ';
out += product;
return out;
}
});
const NMDeviceWired = new Lang.Class({
Name: 'NMDeviceWired',
const NMDeviceSimple = new Lang.Class({
Name: 'NMDeviceSimple',
Extends: NMDevice,
_init: function(client, device, connections) {
this._autoConnectionName = _("Auto Ethernet");
this.category = NMConnectionCategory.WIRED;
this.parent(client, device, connections);
@ -681,6 +656,19 @@ const NMDeviceWired = new Lang.Class({
// we can do it here because addConnection and removeConnection
// both call _createSection at some point
this.section.actor.visible = this._connections.length > 1;
}
});
const NMDeviceWired = new Lang.Class({
Name: 'NMDeviceWired',
Extends: NMDeviceSimple,
_init: function(client, device, connections) {
device._description = _("Wired");
this._autoConnectionName = _("Auto Ethernet");
this.category = NMConnectionCategory.WIRED;
this.parent(client, device, connections);
},
_createAutomaticConnection: function() {
@ -704,12 +692,24 @@ const NMDeviceModem = new Lang.Class({
_init: function(client, device, connections) {
let is_wwan = false;
device._description = _("Mobile broadband");
this._enabled = true;
this.mobileDevice = null;
this._connectionType = 'ppp';
this._capabilities = device.current_capabilities;
if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
// Support new ModemManager1 devices
if (device.udi.indexOf('/org/freedesktop/ModemManager1/Modem') == 0) {
is_wwan = true;
this.mobileDevice = new ModemManager.BroadbandModem(device.udi, device.current_capabilities);
if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
} else if (this._capabilities & NetworkManager.DeviceModemCapabilities.LTE) {
this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
} else if (this._capabilities & NetworkManager.DeviceModemCapabilities.CDMA_EVDO) {
this._connectionType = NetworkManager.SETTING_CDMA_SETTING_NAME;
}
} else if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
is_wwan = true;
this.mobileDevice = new ModemManager.ModemGsm(device.udi);
this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
@ -719,7 +719,8 @@ const NMDeviceModem = new Lang.Class({
this._connectionType = NetworkManager.SETTING_CDMA_SETTING_NAME;
} else if (this._capabilities & NetworkManager.DeviceModemCapabilities.LTE) {
is_wwan = true;
// FIXME: support signal quality
this.mobileDevice = new ModemManager.ModemGsm(device.udi);
this._connectionType = NetworkManager.SETTING_GSM_SETTING_NAME;
}
if (is_wwan) {
@ -822,6 +823,7 @@ const NMDeviceBluetooth = new Lang.Class({
Extends: NMDevice,
_init: function(client, device, connections) {
device._description = _("Bluetooth");
this._autoConnectionName = this._makeConnectionName(device);
device.connect('notify::name', Lang.bind(this, this._updateAutoConnectionName));
@ -864,13 +866,8 @@ const NMDeviceBluetooth = new Lang.Class({
_updateAutoConnectionName: function() {
this._autoConnectionName = this._makeConnectionName(this.device);
this._clearSection();
this._queueCreateSection();
this._updateStatusItem();
},
_getDescription: function() {
return this.device.name || _("Bluetooth");
}
});
@ -1130,10 +1127,8 @@ const NMDeviceWireless = new Lang.Class({
this._networks.splice(res.network, 1);
let newPos = Util.insertSorted(this._networks, network, Lang.bind(this, this._networkSortFunction));
if (newPos != res.network) {
this._clearSection();
if (newPos != res.network)
this._queueCreateSection();
}
},
_accessPointAdded: function(device, accessPoint) {
@ -1186,10 +1181,8 @@ const NMDeviceWireless = new Lang.Class({
let newPos = Util.insertSorted(this._networks, apObj, this._networkSortFunction);
// Queue an update of the UI if we changed the order
if (newPos != pos) {
this._clearSection();
if (newPos != pos)
this._queueCreateSection();
}
},
_accessPointRemoved: function(device, accessPoint) {
@ -1249,12 +1242,10 @@ const NMDeviceWireless = new Lang.Class({
if (res.network < this._networks.length-1)
okNext = this._networkSortFunction(this._networks[res.network + 1], apObj) <= 0;
if (!okPrev || !okNext) {
this._clearSection();
if (!okPrev || !okNext)
this._queueCreateSection();
} else if (apObj.item) {
else if (apObj.item)
apObj.item.updateBestAP(apObj.accessPoints[0]);
}
}
},
@ -1330,7 +1321,6 @@ const NMDeviceWireless = new Lang.Class({
if (forceupdate) {
this._networks.sort(this._networkSortFunction);
this._clearSection();
this._queueCreateSection();
}
},
@ -1363,7 +1353,6 @@ const NMDeviceWireless = new Lang.Class({
if (forceupdate) {
this._networks.sort(this._networkSortFunction);
this._clearSection();
this._queueCreateSection();
}
},
@ -1443,7 +1432,7 @@ const NMDeviceWireless = new Lang.Class({
this.section.addMenuItem(apObj.item, position);
} else {
if (!this._overflowItem) {
this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More..."));
this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More"));
this.section.addMenuItem(this._overflowItem);
}
this._overflowItem.menu.addMenuItem(apObj.item, position - NUM_VISIBLE_NETWORKS);
@ -1474,6 +1463,56 @@ const NMDeviceWireless = new Lang.Class({
},
});
const NMDeviceVirtual = new Lang.Class({
Name: 'NMDeviceVirtual',
Extends: NMDeviceSimple,
_init: function(client, iface, connections) {
this.iface = iface;
this.parent(client, null, connections);
this.category = NMConnectionCategory.VIRTUAL;
},
_shouldShowConnectionList: function() {
return this.hasConnections();
},
connectionValid: function(connection) {
return connection.get_virtual_iface_name() == this.iface;
},
addConnection: function(connection) {
if (!this.device && !this.hasConnections())
this.statusItem.label.text = NMGtk.utils_get_connection_device_name(connection);
this.parent(connection);
},
adoptDevice: function(device) {
if (device.get_iface() == this.iface) {
this._setDevice(device);
if (device._description)
this.syncDescription();
this._updateStatusItem();
this.emit('state-changed');
return true;
} else
return false;
},
removeDevice: function(device) {
if (device == this.device) {
this._setDevice(null);
this._updateStatusItem();
this.emit('state-changed');
}
},
hasConnections: function() {
return this._connections.length != 0;
}
});
const NMVPNSection = new Lang.Class({
Name: 'NMVPNSection',
Extends: NMConnectionBased,
@ -1585,7 +1624,7 @@ const NMVPNSection = new Lang.Class({
if (j >= NUM_VISIBLE_NETWORKS) {
if (!this._overflowItem) {
this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More..."));
this._overflowItem = new PopupMenu.PopupSubMenuMenuItem(_("More"));
this.section.addMenuItem(this._overflowItem);
}
this._overflowItem.menu.addMenuItem(obj.item);
@ -1598,7 +1637,8 @@ const NMVPNSection = new Lang.Class({
},
_connectionStateChanged: function(vpnConnection, newstate, reason) {
if (newstate == NetworkManager.VPNConnectionState.FAILED) {
if (newstate == NetworkManager.VPNConnectionState.FAILED &&
reason != NetworkManager.VPNConnectionStateReason.NO_SECRETS) {
// FIXME: if we ever want to show something based on reason,
// we need to convert from NetworkManager.VPNConnectionStateReason
// to NetworkManager.DeviceStateReason
@ -1629,7 +1669,59 @@ const NMApplet = new Lang.Class({
this.secondaryIcon = this.addIcon(new Gio.ThemedIcon({ name: 'network-vpn-symbolic' }));
this.secondaryIcon.hide();
this._client = NMClient.Client.new();
// Device types
this._dtypes = { };
this._dtypes[NetworkManager.DeviceType.ETHERNET] = NMDeviceWired;
this._dtypes[NetworkManager.DeviceType.WIFI] = NMDeviceWireless;
this._dtypes[NetworkManager.DeviceType.MODEM] = NMDeviceModem;
this._dtypes[NetworkManager.DeviceType.BT] = NMDeviceBluetooth;
this._dtypes[NetworkManager.DeviceType.INFINIBAND] = NMDeviceSimple;
// TODO: WiMax support
// Virtual device types
this._vtypes = { };
if (NMGtk) {
this._vtypes[NetworkManager.SETTING_VLAN_SETTING_NAME] = NMDeviceVirtual;
this._vtypes[NetworkManager.SETTING_BOND_SETTING_NAME] = NMDeviceVirtual;
this._vtypes[NetworkManager.SETTING_BRIDGE_SETTING_NAME] = NMDeviceVirtual;
}
// Connection types
this._ctypes = { };
this._ctypes[NetworkManager.SETTING_WIRELESS_SETTING_NAME] = NMConnectionCategory.WIRELESS;
this._ctypes[NetworkManager.SETTING_WIRED_SETTING_NAME] = NMConnectionCategory.WIRED;
this._ctypes[NetworkManager.SETTING_PPPOE_SETTING_NAME] = NMConnectionCategory.WIRED;
this._ctypes[NetworkManager.SETTING_PPP_SETTING_NAME] = NMConnectionCategory.WIRED;
this._ctypes[NetworkManager.SETTING_BLUETOOTH_SETTING_NAME] = NMConnectionCategory.WWAN;
this._ctypes[NetworkManager.SETTING_CDMA_SETTING_NAME] = NMConnectionCategory.WWAN;
this._ctypes[NetworkManager.SETTING_GSM_SETTING_NAME] = NMConnectionCategory.WWAN;
this._ctypes[NetworkManager.SETTING_INFINIBAND_SETTING_NAME] = NMConnectionCategory.WIRED;
if (NMGtk) {
this._ctypes[NetworkManager.SETTING_VLAN_SETTING_NAME] = NMConnectionCategory.VIRTUAL;
this._ctypes[NetworkManager.SETTING_BOND_SETTING_NAME] = NMConnectionCategory.VIRTUAL;
this._ctypes[NetworkManager.SETTING_BRIDGE_SETTING_NAME] = NMConnectionCategory.VIRTUAL;
}
this._ctypes[NetworkManager.SETTING_VPN_SETTING_NAME] = NMConnectionCategory.VPN;
NMClient.Client.new_async(null, Lang.bind(this, this._clientGot));
NMClient.RemoteSettings.new_async(null, null, Lang.bind(this, this._remoteSettingsGot));
},
_clientGot: function(obj, result) {
this._client = NMClient.Client.new_finish(result);
this._tryLateInit();
},
_remoteSettingsGot: function(obj, result) {
this._settings = NMClient.RemoteSettings.new_finish(result);
this._tryLateInit();
},
_tryLateInit: function() {
if (!this._client || !this._settings)
return;
this._statusSection = new PopupMenu.PopupMenuSection();
this._statusItem = new PopupMenu.PopupMenuItem('', { reactive: false });
@ -1651,23 +1743,32 @@ const NMApplet = new Lang.Class({
this._mobileUpdateId = 0;
this._mobileUpdateDevice = null;
this._nmDevices = [];
this._devices = { };
this._virtualDevices = [ ];
this._devices.wired = {
section: new PopupMenu.PopupMenuSection(),
devices: [ ],
item: new NMWiredSectionTitleMenuItem(_("Wired"))
};
this._devices.wired.section.addMenuItem(this._devices.wired.item);
this._devices.wired.section.actor.hide();
this.menu.addMenuItem(this._devices.wired.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.virtual = {
section: new PopupMenu.PopupMenuSection(),
devices: [ ],
};
this._devices.virtual.section.actor.hide();
this.menu.addMenuItem(this._devices.virtual.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.wireless = {
section: new PopupMenu.PopupMenuSection(),
devices: [ ],
item: this._makeToggleItem('wireless', _("Wireless"))
item: this._makeToggleItem('wireless', _("Wi-Fi"))
};
this._devices.wireless.section.addMenuItem(this._devices.wireless.item);
this._devices.wireless.section.actor.hide();
@ -1677,9 +1778,7 @@ const NMApplet = new Lang.Class({
this._devices.wwan = {
section: new PopupMenu.PopupMenuSection(),
devices: [ ],
item: this._makeToggleItem('wwan', _("Mobile broadband"))
};
this._devices.wwan.section.addMenuItem(this._devices.wwan.item);
this._devices.wwan.section.actor.hide();
this.menu.addMenuItem(this._devices.wwan.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
@ -1690,50 +1789,24 @@ const NMApplet = new Lang.Class({
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop');
// Device types
this._dtypes = { };
this._dtypes[NetworkManager.DeviceType.ETHERNET] = NMDeviceWired;
this._dtypes[NetworkManager.DeviceType.WIFI] = NMDeviceWireless;
this._dtypes[NetworkManager.DeviceType.MODEM] = NMDeviceModem;
this._dtypes[NetworkManager.DeviceType.BT] = NMDeviceBluetooth;
// TODO: WiMax support
this._readConnections();
this._readDevices();
this._syncNMState();
// Connection types
this._ctypes = { };
this._ctypes[NetworkManager.SETTING_WIRELESS_SETTING_NAME] = NMConnectionCategory.WIRELESS;
this._ctypes[NetworkManager.SETTING_WIRED_SETTING_NAME] = NMConnectionCategory.WIRED;
this._ctypes[NetworkManager.SETTING_PPPOE_SETTING_NAME] = NMConnectionCategory.WIRED;
this._ctypes[NetworkManager.SETTING_PPP_SETTING_NAME] = NMConnectionCategory.WIRED;
this._ctypes[NetworkManager.SETTING_BLUETOOTH_SETTING_NAME] = NMConnectionCategory.WWAN;
this._ctypes[NetworkManager.SETTING_CDMA_SETTING_NAME] = NMConnectionCategory.WWAN;
this._ctypes[NetworkManager.SETTING_GSM_SETTING_NAME] = NMConnectionCategory.WWAN;
this._ctypes[NetworkManager.SETTING_VPN_SETTING_NAME] = NMConnectionCategory.VPN;
this._settings = NMClient.RemoteSettings.new(null);
this._connectionsReadId = this._settings.connect('connections-read', Lang.bind(this, function() {
this._readConnections();
this._readDevices();
this._syncNMState();
// Connect to signals late so that early signals don't find in inconsistent state
// and connect only once (this signal handler can be called again if NetworkManager goes up and down)
if (!this._inited) {
this._inited = true;
this._client.connect('notify::manager-running', Lang.bind(this, this._syncNMState));
this._client.connect('notify::networking-enabled', Lang.bind(this, this._syncNMState));
this._client.connect('notify::state', Lang.bind(this, this._syncNMState));
this._client.connect('notify::active-connections', Lang.bind(this, this._updateIcon));
this._client.connect('device-added', Lang.bind(this, this._deviceAdded));
this._client.connect('device-removed', Lang.bind(this, this._deviceRemoved));
this._settings.connect('new-connection', Lang.bind(this, this._newConnection));
}
}));
this._client.connect('notify::manager-running', Lang.bind(this, this._syncNMState));
this._client.connect('notify::networking-enabled', Lang.bind(this, this._syncNMState));
this._client.connect('notify::state', Lang.bind(this, this._syncNMState));
this._client.connect('notify::active-connections', Lang.bind(this, this._updateIcon));
this._client.connect('device-added', Lang.bind(this, this._deviceAdded));
this._client.connect('device-removed', Lang.bind(this, this._deviceRemoved));
this._settings.connect('new-connection', Lang.bind(this, this._newConnection));
},
_ensureSource: function() {
if (!this._source) {
this._source = new MessageTray.Source(_("Network Manager"),
'network-transmit-receive');
this._source.policy = new NotificationDaemon.NotificationApplicationPolicy('gnome-network-panel');
this._source.connect('destroy', Lang.bind(this, function() {
this._source = null;
@ -1758,20 +1831,34 @@ const NMApplet = new Lang.Class({
let devices = this._devices[category].devices;
let item = this._devices[category].item;
let section = this._devices[category].section;
if (devices.length == 0)
let visible;
if (category == NMConnectionCategory.VIRTUAL)
visible = !section.isEmpty();
else
visible = devices.length > 0;
if (!visible)
section.actor.hide();
else {
section.actor.show();
if (devices.length == 1) {
let dev = devices[0];
dev.statusItem.actor.hide();
item.updateForDevice(dev);
} else {
devices.forEach(function(dev) {
dev.statusItem.actor.show();
});
// remove status text from the section title item
item.updateForDevice(null);
// Sync the relation between the section title
// item (the one with the airplane mode switch)
// and the individual device switches
if (item) {
if (devices.length == 1) {
let dev = devices[0];
dev.statusItem.actor.hide();
item.updateForDevice(dev);
} else {
devices.forEach(function(dev) {
dev.statusItem.actor.show();
});
// remove status text from the section title item
item.updateForDevice(null);
}
}
}
},
@ -1779,8 +1866,9 @@ const NMApplet = new Lang.Class({
_readDevices: function() {
let devices = this._client.get_devices() || [ ];
for (let i = 0; i < devices.length; ++i) {
this._deviceAdded(this._client, devices[i]);
this._deviceAdded(this._client, devices[i], true);
}
this._syncDeviceNames();
},
_notifyForDevice: function(device, iconName, title, text, urgency) {
@ -1811,9 +1899,50 @@ const NMApplet = new Lang.Class({
MessageTray.Urgency.HIGH);
},
_makeWrapperDevice: function(wrapperClass, device) {
let wrapper = new wrapperClass(this._client, device, this._connections);
_syncDeviceNames: function() {
if (NMGtk) {
let names = NMGtk.utils_disambiguate_device_names(this._nmDevices);
for (let i = 0; i < this._nmDevices.length; i++) {
let device = this._nmDevices[i];
device._description = names[i];
if (device._delegate)
device._delegate.syncDescription();
}
} else {
for (let i = 0; i < this._nmDevices.length; i++) {
let device = this._nmDevices[i];
device._delegate.syncDescription();
}
}
},
_deviceAdded: function(client, device, skipSyncDeviceNames) {
if (device._delegate) {
// already seen, not adding again
return;
}
for (let i = 0; i < this._virtualDevices.length; i++) {
if (this._virtualDevices[i].adoptDevice(device)) {
this._nmDevices.push(device);
if (!skipSyncDeviceNames)
this._syncDeviceNames();
return;
}
}
let wrapperClass = this._dtypes[device.get_device_type()];
if (wrapperClass) {
let wrapper = new wrapperClass(this._client, device, this._connections);
this._addDeviceWrapper(wrapper);
this._nmDevices.push(device);
if (!skipSyncDeviceNames)
this._syncDeviceNames();
}
},
_addDeviceWrapper: function(wrapper) {
wrapper._activationFailedId = wrapper.connect('activation-failed',
Lang.bind(this, this._onActivationFailed));
wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
@ -1825,35 +1954,36 @@ const NMApplet = new Lang.Class({
wrapper.disconnect(wrapper._destroyId);
});
return wrapper;
},
let section = this._devices[wrapper.category].section;
section.addMenuItem(wrapper.statusItem);
section.addMenuItem(wrapper.section);
_deviceAdded: function(client, device) {
if (device._delegate) {
// already seen, not adding again
return;
}
let wrapperClass = this._dtypes[device.get_device_type()];
if (wrapperClass) {
let wrapper = this._makeWrapperDevice(wrapperClass, device);
let section = this._devices[wrapper.category].section;
let devices = this._devices[wrapper.category].devices;
let devices = this._devices[wrapper.category].devices;
devices.push(wrapper);
section.addMenuItem(wrapper.section, 1);
section.addMenuItem(wrapper.statusItem, 1);
devices.push(wrapper);
this._syncSectionTitle(wrapper.category);
}
this._syncSectionTitle(wrapper.category);
},
_deviceRemoved: function(client, device) {
if (!device._delegate) {
let pos = this._nmDevices.indexOf(device);
if (pos != -1) {
this._nmDevices.splice(pos, 1);
this._syncDeviceNames();
}
let wrapper = device._delegate;
if (!wrapper) {
log('Removing a network device that was not added');
return;
}
let wrapper = device._delegate;
if (wrapper instanceof NMDeviceVirtual)
wrapper.removeDevice(device);
else
this._removeDeviceWrapper(wrapper);
},
_removeDeviceWrapper: function(wrapper) {
wrapper.destroy();
let devices = this._devices[wrapper.category].devices;
@ -2054,6 +2184,13 @@ const NMApplet = new Lang.Class({
devices[i].removeConnection(connection);
}
if (section == NMConnectionCategory.VIRTUAL) {
let iface = connection.get_virtual_iface_name();
let wrapper = this._findVirtualDevice(iface);
if (wrapper && !wrapper.hasConnections())
this._removeDeviceWrapper(wrapper);
}
connection.disconnect(connection._removedId);
connection.disconnect(connection._updatedId);
connection._removedId = connection._updatedId = 0;
@ -2067,6 +2204,27 @@ const NMApplet = new Lang.Class({
let section = connection._section;
if (section == NMConnectionCategory.VIRTUAL) {
let wrapperClass = this._vtypes[connection._type];
if (!wrapperClass)
return;
let iface = connection.get_virtual_iface_name();
let wrapper = this._findVirtualDevice(iface);
if (!wrapper) {
wrapper = new wrapperClass(this._client, iface, this._connections);
this._addDeviceWrapper(wrapper);
this._virtualDevices.push(wrapper);
// We might already have a device for this connection
for (let i = 0; i < this._nmDevices.length; i++) {
let device = this._nmDevices[i];
if (wrapper.adoptDevice(device))
break;
}
}
}
if (section == NMConnectionCategory.INVALID)
return;
if (section == NMConnectionCategory.VPN) {
@ -2079,6 +2237,15 @@ const NMApplet = new Lang.Class({
}
},
_findVirtualDevice: function(iface) {
for (let i = 0; i < this._virtualDevices.length; i++) {
if (this._virtualDevices[i].iface == iface)
return this._virtualDevices[i];
}
return null;
},
_hideDevices: function() {
this._devicesHidden = true;
@ -2094,6 +2261,7 @@ const NMApplet = new Lang.Class({
this._statusSection.actor.hide();
this._syncSectionTitle(NMConnectionCategory.WIRED);
this._syncSectionTitle(NMConnectionCategory.VIRTUAL);
this._syncSectionTitle(NMConnectionCategory.WIRELESS);
this._syncSectionTitle(NMConnectionCategory.WWAN);
},
@ -2131,6 +2299,7 @@ const NMApplet = new Lang.Class({
this.setIcon('network-wireless-acquiring-symbolic');
break;
case NMConnectionCategory.WIRED:
case NMConnectionCategory.VIRTUAL:
this.setIcon('network-wired-acquiring-symbolic');
break;
default:
@ -2170,6 +2339,7 @@ const NMApplet = new Lang.Class({
break;
}
case NMConnectionCategory.WIRED:
case NMConnectionCategory.VIRTUAL:
this.setIcon('network-wired-symbolic');
break;
case NMConnectionCategory.WWAN:

View File

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

View File

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

616
js/ui/switcherPopup.js Normal file
View File

@ -0,0 +1,616 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const POPUP_DELAY_TIMEOUT = 150; // milliseconds
const POPUP_SCROLL_TIME = 0.10; // seconds
const POPUP_FADE_OUT_TIME = 0.1; // seconds
const DISABLE_HOVER_TIMEOUT = 500; // milliseconds
function mod(a, b) {
return (a + b) % b;
}
function primaryModifier(mask) {
if (mask == 0)
return 0;
let primary = 1;
while (mask > 1) {
mask >>= 1;
primary <<= 1;
}
return primary;
}
const SwitcherPopup = new Lang.Class({
Name: 'SwitcherPopup',
Abstract: true,
_init: function(items) {
this._switcherList = null;
this._items = items || [];
this._selectedIndex = 0;
this.actor = new Shell.GenericContainer({ style_class: 'switcher-popup',
reactive: true,
visible: false });
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate));
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
Main.uiGroup.add_actor(this.actor);
this._haveModal = false;
this._modifierMask = 0;
this._motionTimeoutId = 0;
this._initialDelayTimeoutId = 0;
// Initially disable hover so we ignore the enter-event if
// the switcher appears underneath the current pointer location
this._disableHover();
},
_getPreferredWidth: function(actor, forHeight, alloc) {
let primary = Main.layoutManager.primaryMonitor;
alloc.min_size = primary.width;
alloc.natural_size = primary.width;
},
_getPreferredHeight: function(actor, forWidth, alloc) {
let primary = Main.layoutManager.primaryMonitor;
alloc.min_size = primary.height;
alloc.natural_size = primary.height;
},
_allocate: function(actor, box, flags) {
let childBox = new Clutter.ActorBox();
let primary = Main.layoutManager.primaryMonitor;
let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT);
let rightPadding = this.actor.get_theme_node().get_padding(St.Side.RIGHT);
let bottomPadding = this.actor.get_theme_node().get_padding(St.Side.BOTTOM);
let vPadding = this.actor.get_theme_node().get_vertical_padding();
let hPadding = leftPadding + rightPadding;
// Allocate the switcherList
// We select a size based on an icon size that does not overflow the screen
let [childMinHeight, childNaturalHeight] = this._switcherList.actor.get_preferred_height(primary.width - hPadding);
let [childMinWidth, childNaturalWidth] = this._switcherList.actor.get_preferred_width(childNaturalHeight);
childBox.x1 = Math.max(primary.x + leftPadding, primary.x + Math.floor((primary.width - childNaturalWidth) / 2));
childBox.x2 = Math.min(primary.x + primary.width - rightPadding, childBox.x1 + childNaturalWidth);
childBox.y1 = primary.y + Math.floor((primary.height - childNaturalHeight) / 2);
childBox.y2 = childBox.y1 + childNaturalHeight;
this._switcherList.actor.allocate(childBox, flags);
},
_createSwitcher: function() {
throw new Error('Not implemented');
},
_initialSelection: function(backward, binding) {
throw new Error('Not implemented');
},
show: function(backward, binding, mask) {
if (!this._createSwitcher())
return false;
if (!Main.pushModal(this.actor)) {
// Probably someone else has a pointer grab, try again with keyboard only
if (!Main.pushModal(this.actor, { options: Meta.ModalOptions.POINTER_ALREADY_GRABBED })) {
return false;
}
}
this._haveModal = true;
this._modifierMask = primaryModifier(mask);
this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
this.actor.connect('button-press-event', Lang.bind(this, this._clickedOutside));
this.actor.connect('scroll-event', Lang.bind(this, this._scrollEvent));
this.actor.add_actor(this._switcherList.actor);
this._switcherList.connect('item-activated', Lang.bind(this, this._itemActivated));
this._switcherList.connect('item-entered', Lang.bind(this, this._itemEntered));
// Need to force an allocation so we can figure out whether we
// need to scroll when selecting
this.actor.opacity = 0;
this.actor.show();
this.actor.get_allocation_box();
if (this._items.length > 1)
this._selectedIndex = 1;
else
this._selectedIndex = 0;
this._initialSelection(backward, binding);
// There's a race condition; if the user released Alt before
// we got the grab, then we won't be notified. (See
// https://bugzilla.gnome.org/show_bug.cgi?id=596695 for
// details.) So we check now. (Have to do this after updating
// selection.)
let [x, y, mods] = global.get_pointer();
if (!(mods & this._modifierMask)) {
this._finish(global.get_current_time());
return false;
}
// We delay showing the popup so that fast Alt+Tab users aren't
// disturbed by the popup briefly flashing.
this._initialDelayTimeoutId = Mainloop.timeout_add(POPUP_DELAY_TIMEOUT,
Lang.bind(this, function () {
Main.osdWindow.cancel();
this.actor.opacity = 255;
this._initialDelayTimeoutId = 0;
}));
return true;
},
_next: function() {
return mod(this._selectedIndex + 1, this._items.length);
},
_previous: function() {
return mod(this._selectedIndex - 1, this._items.length);
},
_keyPressHandler: function(keysym, backwards, action) {
throw new Error('Not implemented');
},
_keyPressEvent: function(actor, event) {
let keysym = event.get_key_symbol();
let event_state = event.get_state();
let backwards = event_state & Clutter.ModifierType.SHIFT_MASK;
let action = global.display.get_keybinding_action(event.get_key_code(), event_state);
this._disableHover();
if (keysym == Clutter.Escape)
this.destroy();
else
this._keyPressHandler(keysym, backwards, action);
return true;
},
_keyReleaseEvent: function(actor, event) {
let [x, y, mods] = global.get_pointer();
let state = mods & this._modifierMask;
if (state == 0)
this._finish(event.get_time());
return true;
},
_clickedOutside: function(actor, event) {
this.destroy();
},
_scrollHandler: function(direction) {
if (direction == Clutter.ScrollDirection.UP)
this._select(this._previous());
else if (direction == Clutter.ScrollDirection.DOWN)
this._select(this._next());
},
_scrollEvent: function(actor, event) {
this._scrollHandler(event.get_scroll_direction());
},
_itemActivatedHandler: function(n) {
this._select(n);
},
_itemActivated: function(switcher, n) {
this._itemActivatedHandler(n);
this._finish(global.get_current_time());
},
_itemEnteredHandler: function(n) {
this._select(n);
},
_itemEntered: function(switcher, n) {
if (!this.mouseActive)
return;
this._itemEnteredHandler(n);
},
_disableHover: function() {
this.mouseActive = false;
if (this._motionTimeoutId != 0)
Mainloop.source_remove(this._motionTimeoutId);
this._motionTimeoutId = Mainloop.timeout_add(DISABLE_HOVER_TIMEOUT, Lang.bind(this, this._mouseTimedOut));
},
_mouseTimedOut: function() {
this._motionTimeoutId = 0;
this.mouseActive = true;
},
_popModal: function() {
if (this._haveModal) {
Main.popModal(this.actor);
this._haveModal = false;
}
},
destroy: function() {
this._popModal();
if (this.actor.visible) {
Tweener.addTween(this.actor,
{ opacity: 0,
time: POPUP_FADE_OUT_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
this.actor.destroy();
})
});
} else
this.actor.destroy();
},
_finish: function(timestamp) {
this.destroy();
},
_onDestroy: function() {
this._popModal();
if (this._motionTimeoutId != 0)
Mainloop.source_remove(this._motionTimeoutId);
if (this._initialDelayTimeoutId != 0)
Mainloop.source_remove(this._initialDelayTimeoutId);
},
_select: function(num) {
this._selectedIndex = num;
this._switcherList.highlight(num);
}
});
const SwitcherList = new Lang.Class({
Name: 'SwitcherList',
_init : function(squareItems) {
this.actor = new Shell.GenericContainer({ style_class: 'switcher-list' });
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._allocateTop));
// Here we use a GenericContainer so that we can force all the
// children to have the same width.
this._list = new Shell.GenericContainer({ style_class: 'switcher-list-item-container' });
this._list.spacing = 0;
this._list.connect('style-changed', Lang.bind(this, function() {
this._list.spacing = this._list.get_theme_node().get_length('spacing');
}));
this._list.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this._list.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this._list.connect('allocate', Lang.bind(this, this._allocate));
this._scrollView = new St.ScrollView({ style_class: 'hfade',
enable_mouse_scrolling: false });
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.NEVER);
let scrollBox = new St.BoxLayout();
scrollBox.add_actor(this._list);
this._scrollView.add_actor(scrollBox);
this.actor.add_actor(this._scrollView);
// Those arrows indicate whether scrolling in one direction is possible
this._leftArrow = new St.DrawingArea({ style_class: 'switcher-arrow',
pseudo_class: 'highlighted' });
this._leftArrow.connect('repaint', Lang.bind(this,
function() { drawArrow(this._leftArrow, St.Side.LEFT); }));
this._rightArrow = new St.DrawingArea({ style_class: 'switcher-arrow',
pseudo_class: 'highlighted' });
this._rightArrow.connect('repaint', Lang.bind(this,
function() { drawArrow(this._rightArrow, St.Side.RIGHT); }));
this.actor.add_actor(this._leftArrow);
this.actor.add_actor(this._rightArrow);
this._items = [];
this._highlighted = -1;
this._squareItems = squareItems;
this._minSize = 0;
this._scrollableRight = true;
this._scrollableLeft = false;
},
_allocateTop: function(actor, box, flags) {
let leftPadding = this.actor.get_theme_node().get_padding(St.Side.LEFT);
let rightPadding = this.actor.get_theme_node().get_padding(St.Side.RIGHT);
let childBox = new Clutter.ActorBox();
let scrollable = this._minSize > box.x2 - box.x1;
box.y1 -= this.actor.get_theme_node().get_padding(St.Side.TOP);
box.y2 += this.actor.get_theme_node().get_padding(St.Side.BOTTOM);
this._scrollView.allocate(box, flags);
let arrowWidth = Math.floor(leftPadding / 3);
let arrowHeight = arrowWidth * 2;
childBox.x1 = leftPadding / 2;
childBox.y1 = this.actor.height / 2 - arrowWidth;
childBox.x2 = childBox.x1 + arrowWidth;
childBox.y2 = childBox.y1 + arrowHeight;
this._leftArrow.allocate(childBox, flags);
this._leftArrow.opacity = (this._scrollableLeft && scrollable) ? 255 : 0;
arrowWidth = Math.floor(rightPadding / 3);
arrowHeight = arrowWidth * 2;
childBox.x1 = this.actor.width - arrowWidth - rightPadding / 2;
childBox.y1 = this.actor.height / 2 - arrowWidth;
childBox.x2 = childBox.x1 + arrowWidth;
childBox.y2 = childBox.y1 + arrowHeight;
this._rightArrow.allocate(childBox, flags);
this._rightArrow.opacity = (this._scrollableRight && scrollable) ? 255 : 0;
},
addItem : function(item, label) {
let bbox = new St.Button({ style_class: 'item-box',
reactive: true });
bbox.set_child(item);
this._list.add_actor(bbox);
let n = this._items.length;
bbox.connect('clicked', Lang.bind(this, function() { this._onItemClicked(n); }));
bbox.connect('enter-event', Lang.bind(this, function() { this._onItemEnter(n); }));
bbox.label_actor = label;
this._items.push(bbox);
return bbox;
},
_onItemClicked: function (index) {
this._itemActivated(index);
},
_onItemEnter: function (index) {
this._itemEntered(index);
},
highlight: function(index, justOutline) {
if (this._highlighted != -1) {
this._items[this._highlighted].remove_style_pseudo_class('outlined');
this._items[this._highlighted].remove_style_pseudo_class('selected');
}
this._highlighted = index;
if (this._highlighted != -1) {
if (justOutline)
this._items[this._highlighted].add_style_pseudo_class('outlined');
else
this._items[this._highlighted].add_style_pseudo_class('selected');
}
let adjustment = this._scrollView.hscroll.adjustment;
let [value, lower, upper, stepIncrement, pageIncrement, pageSize] = adjustment.get_values();
let [absItemX, absItemY] = this._items[index].get_transformed_position();
let [result, posX, posY] = this.actor.transform_stage_point(absItemX, 0);
let [containerWidth, containerHeight] = this.actor.get_transformed_size();
if (posX + this._items[index].get_width() > containerWidth)
this._scrollToRight();
else if (this._items[index].allocation.x1 - value < 0)
this._scrollToLeft();
},
_scrollToLeft : function() {
let adjustment = this._scrollView.hscroll.adjustment;
let [value, lower, upper, stepIncrement, pageIncrement, pageSize] = adjustment.get_values();
let item = this._items[this._highlighted];
if (item.allocation.x1 < value)
value = Math.min(0, item.allocation.x1);
else if (item.allocation.x2 > value + pageSize)
value = Math.max(upper, item.allocation.x2 - pageSize);
this._scrollableRight = true;
Tweener.addTween(adjustment,
{ value: value,
time: POPUP_SCROLL_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function () {
if (this._highlighted == 0) {
this._scrollableLeft = false;
this.actor.queue_relayout();
}
})
});
},
_scrollToRight : function() {
let adjustment = this._scrollView.hscroll.adjustment;
let [value, lower, upper, stepIncrement, pageIncrement, pageSize] = adjustment.get_values();
let item = this._items[this._highlighted];
if (item.allocation.x1 < value)
value = Math.max(0, item.allocation.x1);
else if (item.allocation.x2 > value + pageSize)
value = Math.min(upper, item.allocation.x2 - pageSize);
this._scrollableLeft = true;
Tweener.addTween(adjustment,
{ value: value,
time: POPUP_SCROLL_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function () {
if (this._highlighted == this._items.length - 1) {
this._scrollableRight = false;
this.actor.queue_relayout();
}
})
});
},
_itemActivated: function(n) {
this.emit('item-activated', n);
},
_itemEntered: function(n) {
this.emit('item-entered', n);
},
_maxChildWidth: function (forHeight) {
let maxChildMin = 0;
let maxChildNat = 0;
for (let i = 0; i < this._items.length; i++) {
let [childMin, childNat] = this._items[i].get_preferred_width(forHeight);
maxChildMin = Math.max(childMin, maxChildMin);
maxChildNat = Math.max(childNat, maxChildNat);
if (this._squareItems) {
let [childMin, childNat] = this._items[i].get_preferred_height(-1);
maxChildMin = Math.max(childMin, maxChildMin);
maxChildNat = Math.max(childNat, maxChildNat);
}
}
return [maxChildMin, maxChildNat];
},
_getPreferredWidth: function (actor, forHeight, alloc) {
let [maxChildMin, maxChildNat] = this._maxChildWidth(forHeight);
let totalSpacing = this._list.spacing * (this._items.length - 1);
alloc.min_size = this._items.length * maxChildMin + totalSpacing;
alloc.natural_size = alloc.min_size;
this._minSize = alloc.min_size;
},
_getPreferredHeight: function (actor, forWidth, alloc) {
let maxChildMin = 0;
let maxChildNat = 0;
for (let i = 0; i < this._items.length; i++) {
let [childMin, childNat] = this._items[i].get_preferred_height(-1);
maxChildMin = Math.max(childMin, maxChildMin);
maxChildNat = Math.max(childNat, maxChildNat);
}
if (this._squareItems) {
let [childMin, childNat] = this._maxChildWidth(-1);
maxChildMin = Math.max(childMin, maxChildMin);
maxChildNat = maxChildMin;
}
alloc.min_size = maxChildMin;
alloc.natural_size = maxChildNat;
},
_allocate: function (actor, box, flags) {
let childHeight = box.y2 - box.y1;
let [maxChildMin, maxChildNat] = this._maxChildWidth(childHeight);
let totalSpacing = this._list.spacing * (this._items.length - 1);
let childWidth = Math.floor(Math.max(0, box.x2 - box.x1 - totalSpacing) / this._items.length);
let x = 0;
let children = this._list.get_children();
let childBox = new Clutter.ActorBox();
let primary = Main.layoutManager.primaryMonitor;
let parentRightPadding = this.actor.get_parent().get_theme_node().get_padding(St.Side.RIGHT);
for (let i = 0; i < children.length; i++) {
if (this._items.indexOf(children[i]) != -1) {
let [childMin, childNat] = children[i].get_preferred_height(childWidth);
let vSpacing = (childHeight - childNat) / 2;
childBox.x1 = x;
childBox.y1 = vSpacing;
childBox.x2 = x + childWidth;
childBox.y2 = childBox.y1 + childNat;
children[i].allocate(childBox, flags);
x += this._list.spacing + childWidth;
} else {
// Something else, eg, AppSwitcher's arrows;
// we don't allocate it.
}
}
}
});
Signals.addSignalMethods(SwitcherList.prototype);
function drawArrow(area, side) {
let themeNode = area.get_theme_node();
let borderColor = themeNode.get_border_color(side);
let bodyColor = themeNode.get_foreground_color();
let [width, height] = area.get_surface_size ();
let cr = area.get_context();
cr.setLineWidth(1.0);
Clutter.cairo_set_source_color(cr, borderColor);
switch (side) {
case St.Side.TOP:
cr.moveTo(0, height);
cr.lineTo(Math.floor(width * 0.5), 0);
cr.lineTo(width, height);
break;
case St.Side.BOTTOM:
cr.moveTo(width, 0);
cr.lineTo(Math.floor(width * 0.5), height);
cr.lineTo(0, 0);
break;
case St.Side.LEFT:
cr.moveTo(width, height);
cr.lineTo(0, Math.floor(height * 0.5));
cr.lineTo(width, 0);
break;
case St.Side.RIGHT:
cr.moveTo(0, 0);
cr.lineTo(width, Math.floor(height * 0.5));
cr.lineTo(0, height);
break;
}
cr.strokePreserve();
Clutter.cairo_set_source_color(cr, bodyColor);
cr.fill();
cr.$dispose();
}

View File

@ -2,6 +2,7 @@
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
@ -73,6 +74,9 @@ function _wrapTweening(target, tweeningParameters) {
}
}
if (!Gtk.Settings.get_default().gtk_enable_animations)
tweeningParameters['time'] = 0.000001;
_addHandler(target, tweeningParameters, 'onStart', _tweenStarted);
_addHandler(target, tweeningParameters, 'onComplete', _tweenCompleted);
}

View File

@ -14,12 +14,15 @@ const St = imports.gi.St;
const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const Panel = imports.ui.panel;
const ShellEntry = imports.ui.shellEntry;
const Tweener = imports.ui.tweener;
const UserMenu = imports.ui.userMenu;
const UserWidget = imports.ui.userWidget;
const Batch = imports.gdm.batch;
const GdmUtil = imports.gdm.util;
const LoginDialog = imports.gdm.loginDialog;
// The timeout before going back automatically to the lock screen (in seconds)
const IDLE_TIMEOUT = 2 * 60;
@ -53,59 +56,6 @@ function isSupported() {
}
}
// A widget showing the user avatar and name
const UserWidget = new Lang.Class({
Name: 'UserWidget',
_init: function(user) {
this._user = user;
this.actor = new St.BoxLayout({ style_class: 'unlock-dialog-user-name-container',
vertical: false });
this._avatar = new UserMenu.UserAvatarWidget(user);
this.actor.add(this._avatar.actor,
{ x_fill: true, y_fill: true });
this._label = new St.Label({ style_class: 'login-dialog-username' });
this.actor.add(this._label,
{ expand: true,
x_fill: true,
y_fill: false,
y_align: St.Align.MIDDLE });
this._userLoadedId = this._user.connect('notify::is-loaded',
Lang.bind(this, this._updateUser));
this._userChangedId = this._user.connect('changed',
Lang.bind(this, this._updateUser));
if (this._user.is_loaded)
this._updateUser();
},
destroy: function() {
if (this._userLoadedId != 0) {
this._user.disconnect(this._userLoadedId);
this._userLoadedId = 0;
}
if (this._userChangedId != 0) {
this._user.disconnect(this._userChangedId);
this._userChangedId = 0;
}
this.actor.destroy();
},
_updateUser: function() {
if (this._user.is_loaded)
this._label.text = this._user.get_real_name();
else
this._label.text = '';
this._avatar.update();
}
});
const UnlockDialog = new Lang.Class({
Name: 'UnlockDialog',
Extends: ModalDialog.ModalDialog,
@ -113,6 +63,7 @@ const UnlockDialog = new Lang.Class({
_init: function(parentActor) {
this.parent({ shellReactive: true,
styleClass: 'login-dialog',
keybindingMode: Shell.KeyBindingMode.UNLOCK_SCREEN,
parentActor: parentActor
});
@ -135,7 +86,7 @@ const UnlockDialog = new Lang.Class({
this._userVerifier.connect('show-login-hint', Lang.bind(this, this._showLoginHint));
this._userVerifier.connect('hide-login-hint', Lang.bind(this, this._hideLoginHint));
this._userWidget = new UserWidget(this._user);
this._userWidget = new UserWidget.UserWidget(this._user);
this.contentLayout.add_actor(this._userWidget.actor);
this._promptLayout = new St.BoxLayout({ style_class: 'login-dialog-prompt-layout',
@ -150,7 +101,9 @@ const UnlockDialog = new Lang.Class({
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));
this._promptEntry.clutter_text.connect('text-changed', Lang.bind(this, function() {
this._updateOkButtonSensitivity(this._promptEntry.text.length > 0);
}));
this._promptLayout.add(this._promptEntry,
{ expand: true,
@ -165,29 +118,54 @@ const UnlockDialog = new Lang.Class({
this._promptLoginHint.hide();
this.contentLayout.add_actor(this._promptLoginHint);
let cancelButton = { label: _("Cancel"),
action: Lang.bind(this, this._escape),
key: Clutter.KEY_Escape };
this._okButton = { label: _("Unlock"),
action: Lang.bind(this, this._doUnlock),
default: true };
this.setButtons([cancelButton, this._okButton]);
this._workSpinner = new Panel.AnimatedIcon('process-working.svg', LoginDialog.WORK_SPINNER_ICON_SIZE);
this._workSpinner.actor.opacity = 0;
this.allowCancel = false;
this.buttonLayout.visible = true;
this.addButton({ label: _("Cancel"),
action: Lang.bind(this, this._escape),
key: Clutter.KEY_Escape },
{ expand: true,
x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
this.buttonLayout.add(this._workSpinner.actor,
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
this._okButton = this.addButton({ label: _("Unlock"),
action: Lang.bind(this, this._doUnlock),
default: true },
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
let screenSaverSettings = new Gio.Settings({ schema: 'org.gnome.desktop.screensaver' });
if (screenSaverSettings.get_boolean('user-switch-enabled')) {
let otherUserLabel = new St.Label({ text: _("Log in as another user"),
style_class: 'login-dialog-not-listed-label' });
this._otherUserButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
can_focus: true,
child: otherUserLabel,
reactive: true,
x_align: St.Align.START,
x_fill: true });
this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
this.dialogLayout.add(this._otherUserButton,
{ x_align: St.Align.START,
x_fill: false });
} else {
this._otherUserButton = null;
}
this._updateSensitivity(true);
let otherUserLabel = new St.Label({ text: _("Log in as another user"),
style_class: 'login-dialog-not-listed-label' });
this._otherUserButton = new St.Button({ style_class: 'login-dialog-not-listed-button',
can_focus: true,
child: otherUserLabel,
reactive: true,
x_align: St.Align.START,
x_fill: true });
this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked));
this.dialogLayout.add(this._otherUserButton,
{ x_align: St.Align.START,
x_fill: false });
let batch = new Batch.Hold();
this._userVerifier.begin(this._userName, batch);
@ -196,15 +174,47 @@ const UnlockDialog = new Lang.Class({
return false;
}));
Main.ctrlAltTabManager.addGroup(this.dialogLayout, _("Unlock Window"), 'dialog-password-symbolic');
this._idleMonitor = new GnomeDesktop.IdleMonitor();
this._idleWatchId = this._idleMonitor.add_watch(IDLE_TIMEOUT * 1000, Lang.bind(this, this._escape));
this._idleWatchId = this._idleMonitor.add_idle_watch(IDLE_TIMEOUT * 1000, Lang.bind(this, this._escape));
},
_updateSensitivity: function(sensitive) {
this._promptEntry.reactive = sensitive;
this._promptEntry.clutter_text.editable = sensitive;
this._okButton.button.reactive = sensitive;
this._okButton.button.can_focus = sensitive;
this._updateOkButtonSensitivity(sensitive && this._promptEntry.text.length > 0);
if (this._otherUserButton) {
this._otherUserButton.reactive = sensitive;
this._otherUserButton.can_focus = sensitive;
}
},
_updateOkButtonSensitivity: function(sensitive) {
this._okButton.reactive = sensitive;
this._okButton.can_focus = sensitive;
},
_setWorking: function(working) {
if (working) {
this._workSpinner.play();
Tweener.addTween(this._workSpinner.actor,
{ opacity: 255,
delay: LoginDialog.WORK_SPINNER_ANIMATION_DELAY,
time: LoginDialog.WORK_SPINNER_ANIMATION_TIME,
transition: 'linear'
});
} else {
Tweener.addTween(this._workSpinner.actor,
{ opacity: 0,
time: LoginDialog.WORK_SPINNER_ANIMATION_TIME,
transition: 'linear',
onCompleteScope: this,
onComplete: function() {
this._workSpinner.stop();
}
});
}
},
_showMessage: function(userVerifier, message, styleClass) {
@ -237,6 +247,7 @@ const UnlockDialog = new Lang.Class({
this._currentQuery = serviceName;
this._updateSensitivity(true);
this._setWorking(false);
},
_showLoginHint: function(verifier, message) {
@ -255,6 +266,7 @@ const UnlockDialog = new Lang.Class({
// the actual reply to GDM will be sent as soon as asked
this._firstQuestionAnswer = this._promptEntry.text;
this._updateSensitivity(false);
this._setWorking(true);
return;
}
@ -265,6 +277,7 @@ const UnlockDialog = new Lang.Class({
this._currentQuery = null;
this._updateSensitivity(false);
this._setWorking(true);
this._userVerifier.answerQuery(query, this._promptEntry.text);
},
@ -287,11 +300,14 @@ const UnlockDialog = new Lang.Class({
this._promptEntry.menu.isPassword = true;
this._updateSensitivity(false);
this._setWorking(false);
},
_escape: function() {
this._userVerifier.cancel();
this.emit('failed');
if (this.allowCancel) {
this._userVerifier.cancel();
this.emit('failed');
}
},
_otherUserClicked: function(button, event) {

View File

@ -4,17 +4,20 @@ const AccountsService = imports.gi.AccountsService;
const Gdm = imports.gi.Gdm;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Tp = imports.gi.TelepathyGLib;
const Atk = imports.gi.Atk;
const Clutter = imports.gi.Clutter;
const BoxPointer = imports.ui.boxpointer;
const GnomeSession = imports.misc.gnomeSession;
const LoginManager = imports.misc.loginManager;
const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Params = imports.misc.params;
@ -22,14 +25,17 @@ const Util = imports.misc.util;
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
const PRIVACY_SCHEMA = 'org.gnome.desktop.privacy'
const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
const DISABLE_LOG_OUT_KEY = 'disable-log-out';
const LOCK_ENABLED_KEY = 'lock-enabled';
const ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out';
const SHOW_FULL_NAME_IN_TOP_BAR_KEY = 'show-full-name-in-top-bar';
const DIALOG_ICON_SIZE = 64;
const MAX_USERS_IN_SESSION_DIALOG = 5;
const IMStatus = {
AVAILABLE: 0,
BUSY: 1,
@ -40,6 +46,17 @@ const IMStatus = {
LAST: 6
};
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
<property name="Id" type="s" access="read"/>
<property name="Remote" type="b" access="read"/>
<property name="Class" type="s" access="read"/>
<property name="Type" type="s" access="read"/>
<property name="State" type="s" access="read"/>
</interface>;
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
// Adapted from gdm/gui/user-switch-applet/applet.c
//
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
@ -67,7 +84,7 @@ const UserAvatarWidget = new Lang.Class({
update: function() {
let iconFile = this._user.get_icon_file();
if (!GLib.file_test(iconFile, GLib.FileTest.EXISTS))
if (iconFile && !GLib.file_test(iconFile, GLib.FileTest.EXISTS))
iconFile = null;
if (iconFile) {
@ -476,6 +493,7 @@ const UserMenuButton = new Lang.Class({
this._screenSaverSettings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
this._privacySettings = new Gio.Settings({ schema: PRIVACY_SCHEMA });
this._userManager = AccountsService.UserManager.get_default();
@ -550,9 +568,14 @@ const UserMenuButton = new Lang.Class({
Lang.bind(this, this._updateSwitchUser));
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
Lang.bind(this, this._updateLogout));
this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
Lang.bind(this, this._updateLockScreen));
global.settings.connect('changed::' + ALWAYS_SHOW_LOG_OUT_KEY,
Lang.bind(this, this._updateLogout));
this._screenSaverSettings.connect('changed::' + SHOW_FULL_NAME_IN_TOP_BAR_KEY,
Lang.bind(this, this._updateUserName));
this._privacySettings.connect('changed::' + SHOW_FULL_NAME_IN_TOP_BAR_KEY,
Lang.bind(this, this._updateUserName));
this._updateSwitchUser();
this._updateLogout();
this._updateLockScreen();
@ -577,6 +600,7 @@ const UserMenuButton = new Lang.Class({
Lang.bind(this, this._updateHaveShutdown));
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
Main.screenShield.connect('locked-changed', Lang.bind(this, this._updatePresenceIcon));
this._sessionUpdated();
},
@ -589,6 +613,7 @@ const UserMenuButton = new Lang.Class({
this.setSensitive(!Main.sessionMode.isLocked);
this._updatePresenceIcon();
this._updateUserName();
},
_onDestroy: function() {
@ -597,7 +622,10 @@ const UserMenuButton = new Lang.Class({
},
_updateUserName: function() {
if (this._user.is_loaded)
let settings = this._privacySettings;
if (Main.sessionMode.isLocked)
settings = this._screenSaverSettings;
if (this._user.is_loaded && settings.get_boolean(SHOW_FULL_NAME_IN_TOP_BAR_KEY))
this._name.set_text(this._user.get_real_name());
else
this._name.set_text("");
@ -692,6 +720,11 @@ const UserMenuButton = new Lang.Class({
this._iconBox.child = this._idleIcon;
else
this._iconBox.child = this._offlineIcon;
if (Main.sessionMode.isLocked)
this._iconBox.visible = Main.screenShield.locked;
else
this._iconBox.visible = true;
},
_setupAccounts: function() {
@ -752,7 +785,7 @@ const UserMenuButton = new Lang.Class({
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("System Settings"));
item = new PopupMenu.PopupMenuItem(_("Settings"));
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
this.menu.addMenuItem(item);
this._systemSettings = item;
@ -812,7 +845,7 @@ const UserMenuButton = new Lang.Class({
_onMyAccountActivate: function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().lookup_setting('gnome-user-accounts-panel.desktop');
let app = Shell.AppSystem.get_default().lookup_app('gnome-user-accounts-panel.desktop');
app.activate();
},
@ -847,25 +880,115 @@ const UserMenuButton = new Lang.Class({
this._session.RebootRemote();
},
_openSessionWarnDialog: function(sessions) {
let dialog = new ModalDialog.ModalDialog();
let subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject',
text: _("Other users are logged in.") });
dialog.contentLayout.add(subjectLabel, { y_fill: true,
y_align: St.Align.START });
let descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description'});
descriptionLabel.set_text(_("Shutting down might cause them to lose unsaved work."));
descriptionLabel.clutter_text.line_wrap = true;
dialog.contentLayout.add(descriptionLabel, { x_fill: true,
y_fill: true,
y_align: St.Align.START });
let scrollView = new St.ScrollView({ style_class: 'end-session-dialog-app-list' });
scrollView.add_style_class_name('vfade');
scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
dialog.contentLayout.add(scrollView, { x_fill: true, y_fill: true });
let userList = new St.BoxLayout({ vertical: true });
scrollView.add_actor(userList);
for (let i = 0; i < sessions.length; i++) {
let session = sessions[i];
let userEntry = new St.BoxLayout({ style_class: 'login-dialog-user-list-item',
vertical: false });
let avatar = new UserAvatarWidget(session.user);
avatar.update();
userEntry.add(avatar.actor);
let userLabelText = "";;
let userName = session.user.get_real_name() ?
session.user.get_real_name() : session.username;
if (session.info.remote)
userLabelText = _("%s (remote)").format(userName);
else if (session.info.type == "tty")
userLabelText = _("%s (console)").format(userName);
else
userLabelText = userName;
let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
vertical: true });
textLayout.add(new St.Label({ text: userLabelText }),
{ y_fill: false,
y_align: St.Align.MIDDLE,
expand: true });
userEntry.add(textLayout, { expand: true });
userList.add(userEntry, { x_fill: true });
}
let cancelButton = { label: _("Cancel"),
action: function() { dialog.close(); },
key: Clutter.Escape };
let powerOffButton = { label: _("Power Off"), action: Lang.bind(this, function() {
dialog.close();
this._session.ShutdownRemote();
}), default: true };
dialog.setButtons([cancelButton, powerOffButton]);
dialog.open();
},
_onSuspendOrPowerOffActivate: function() {
Main.overview.hide();
if (this._haveShutdown &&
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
this._session.ShutdownRemote();
this._loginManager.listSessions(Lang.bind(this,
function(result) {
let sessions = [];
let n = 0;
for (let i = 0; i < result.length; i++) {
let[id, uid, userName, seat, sessionPath] = result[i];
let proxy = new SystemdLoginSession(Gio.DBus.system,
'org.freedesktop.login1',
sessionPath);
if (proxy.Class != 'user')
continue;
if (proxy.State == 'closing')
continue;
if (proxy.Id == GLib.getenv('XDG_SESSION_ID'))
continue;
sessions.push({ user: this._userManager.get_user(userName),
username: userName,
info: { type: proxy.Type,
remote: proxy.Remote }
});
// limit the number of entries
n++;
if (n == MAX_USERS_IN_SESSION_DIALOG)
break;
}
if (n != 0)
this._openSessionWarnDialog(sessions);
else
this._session.ShutdownRemote();
}));
} else {
if (this._screenSaverSettings.get_boolean(LOCK_ENABLED_KEY)) {
let tmpId = Main.screenShield.connect('lock-screen-shown', Lang.bind(this, function() {
Main.screenShield.disconnect(tmpId);
this._loginManager.suspend();
}));
this.menu.close(BoxPointer.PopupAnimation.NONE);
Main.screenShield.lock(true);
} else {
this._loginManager.suspend();
}
this.menu.close(BoxPointer.PopupAnimation.NONE);
this._loginManager.suspend();
}
}
});

61
js/ui/userWidget.js Normal file
View File

@ -0,0 +1,61 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
//
// A widget showing the user avatar and name
const AccountsService = imports.gi.AccountsService;
const Lang = imports.lang;
const St = imports.gi.St;
const UserMenu = imports.ui.userMenu;
const UserWidget = new Lang.Class({
Name: 'UserWidget',
_init: function(user) {
this._user = user;
this.actor = new St.BoxLayout({ style_class: 'user-widget',
vertical: false });
this._avatar = new UserMenu.UserAvatarWidget(user);
this.actor.add(this._avatar.actor,
{ x_fill: true, y_fill: true });
this._label = new St.Label({ style_class: 'user-widget-label' });
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',
Lang.bind(this, this._updateUser));
this._userChangedId = this._user.connect('changed',
Lang.bind(this, this._updateUser));
if (this._user.is_loaded)
this._updateUser();
},
destroy: function() {
if (this._userLoadedId != 0) {
this._user.disconnect(this._userLoadedId);
this._userLoadedId = 0;
}
if (this._userChangedId != 0) {
this._user.disconnect(this._userChangedId);
this._userChangedId = 0;
}
this.actor.destroy();
},
_updateUser: function() {
if (this._user.is_loaded)
this._label.text = this._user.get_real_name();
else
this._label.text = '';
this._avatar.update();
}
});

View File

@ -12,6 +12,8 @@ const St = imports.gi.St;
const AppDisplay = imports.ui.appDisplay;
const Main = imports.ui.main;
const OverviewControls = imports.ui.overviewControls;
const Params = imports.misc.params;
const RemoteSearch = imports.ui.remoteSearch;
const Search = imports.ui.search;
const SearchDisplay = imports.ui.searchDisplay;
@ -22,6 +24,12 @@ const WorkspacesView = imports.ui.workspacesView;
const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
const ViewPage = {
WINDOWS: 1,
APPS: 2,
SEARCH: 3
};
const FocusTrap = new Lang.Class({
Name: 'FocusTrap',
Extends: St.Widget,
@ -34,26 +42,28 @@ const FocusTrap = new Lang.Class({
}
});
function getTermsForSearchString(searchString) {
searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
if (searchString == '')
return [];
let terms = searchString.split(/\s+/);
return terms;
}
const ViewSelector = new Lang.Class({
Name: 'ViewSelector',
_init : function(searchEntry, showAppsButton) {
this.actor = new St.BoxLayout({ name: 'viewSelector',
vertical: true });
this.actor = new Shell.Stack({ name: 'viewSelector' });
this._showAppsBlocked = false;
this._showAppsButton = showAppsButton;
this._showAppsButton.connect('notify::checked', Lang.bind(this, this._onShowAppsButtonToggled));
this._pageArea = new Shell.Stack();
this.actor.add(this._pageArea, { x_fill: true,
y_fill: true,
expand: true });
this._activePage = null;
this.active = false;
this._searchPending = false;
this._searchActive = false;
this._searchTimeoutId = 0;
this._searchSystem = new Search.SearchSystem();
@ -73,32 +83,36 @@ const ViewSelector = new Lang.Class({
this._entry.connect('notify::mapped', Lang.bind(this, this._onMapped));
global.stage.connect('notify::key-focus', Lang.bind(this, this._onStageKeyFocusChanged));
this._inactiveIcon = new St.Icon({ style_class: 'search-entry-icon',
icon_name: 'edit-find-symbolic' });
this._activeIcon = new St.Icon({ style_class: 'search-entry-icon',
icon_name: 'edit-clear-symbolic' });
this._entry.set_secondary_icon(this._inactiveIcon);
this._entry.set_primary_icon(new St.Icon({ style_class: 'search-entry-icon',
icon_name: 'edit-find-symbolic' }));
this._clearIcon = new St.Icon({ style_class: 'search-entry-icon',
icon_name: 'edit-clear-symbolic' });
this._iconClickedId = 0;
this._capturedEventId = 0;
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
this._workspacesPage = this._addPage(this._workspacesDisplay.actor, null,
_("Windows"), 'text-x-generic-symbolic');
this._workspacesPage = this._addPage(this._workspacesDisplay.actor,
_("Windows"), 'emblem-documents-symbolic');
this._appDisplay = new AppDisplay.AllAppDisplay();
this._appsPage = this._addPage(this._appDisplay.actor, null,
_("Applications"), 'system-run-symbolic');
this._appDisplay = new AppDisplay.AppDisplay();
this._appsPage = this._addPage(this._appDisplay.actor,
_("Applications"), 'view-grid-symbolic');
this._searchResults = new SearchDisplay.SearchResults(this._searchSystem);
this._searchPage = this._addPage(this._searchResults.actor, this._entry,
_("Search"), 'edit-find-symbolic');
this._searchPage = this._addPage(this._searchResults.actor,
_("Search"), 'edit-find-symbolic',
{ a11yFocus: this._entry });
this._searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
this._searchSettings.connect('changed::disabled', Lang.bind(this, this._reloadRemoteProviders));
this._searchSettings.connect('changed::disable-external', Lang.bind(this, this._reloadRemoteProviders));
this._searchSettings.connect('changed::sort-order', Lang.bind(this, this._reloadRemoteProviders));
// Default search providers
// Wanda comes obviously first
this.addSearchProvider(new Wanda.WandaSearchProvider());
this.addSearchProvider(new AppDisplay.AppSearchProvider());
this.addSearchProvider(new AppDisplay.SettingsSearchProvider());
// Load remote search providers provided by applications
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));
@ -114,9 +128,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,31 +144,23 @@ 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));
Main.wm.addKeybinding('toggle-application-view',
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.NONE,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._toggleAppsPage));
},
_showWithAppsPage: function() {
_toggleAppsPage: function() {
Main.overview.show();
this._showAppsButton.set_checked(true);
this._showAppsButton.checked = !this._showAppsButton.checked;
},
show: function() {
this._activePage = this._workspacesPage;
this.reset();
this._appsPage.hide();
this._searchPage.hide();
this._workspacesDisplay.show();
@ -165,7 +168,7 @@ const ViewSelector = new Lang.Class({
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows())
Main.overview.fadeOutDesktop();
this._showPage(this._workspacesPage);
this._showPage(this._workspacesPage, true);
},
zoomFromOverview: function() {
@ -179,14 +182,16 @@ const ViewSelector = new Lang.Class({
this._workspacesDisplay.hide();
},
_addPage: function(actor, a11yFocus, name, a11yIcon) {
_addPage: function(actor, name, a11yIcon, params) {
params = Params.parse(params, { a11yFocus: null });
let page = new St.Bin({ child: actor,
x_align: St.Align.START,
y_align: St.Align.START,
x_fill: true,
y_fill: true });
if (a11yFocus)
Main.ctrlAltTabManager.addGroup(a11yFocus, name, a11yIcon);
if (params.a11yFocus)
Main.ctrlAltTabManager.addGroup(params.a11yFocus, name, a11yIcon);
else
Main.ctrlAltTabManager.addGroup(actor, name, a11yIcon,
{ proxy: this.actor,
@ -195,33 +200,44 @@ const ViewSelector = new Lang.Class({
this._a11yFocusPage(page);
})
});;
this._pageArea.add_actor(page);
return page
this.actor.add_actor(page);
return page;
},
_showPage: function(page) {
if(page == this._activePage)
_fadePageIn: function(oldPage) {
if (oldPage)
oldPage.hide();
this.emit('page-empty');
this._activePage.show();
Tweener.addTween(this._activePage,
{ opacity: 255,
time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
_showPage: function(page, noFade) {
if (page == this._activePage)
return;
if(this._activePage) {
Tweener.addTween(this._activePage,
let oldPage = this._activePage;
this._activePage = page;
this.emit('page-changed');
if (oldPage && !noFade)
Tweener.addTween(oldPage,
{ opacity: 0,
time: 0.1,
time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
this._activePage.hide();
this._activePage = page;
this._fadePageIn(oldPage);
})
});
}
page.show();
Tweener.addTween(page,
{ opacity: 255,
time: 0.1,
transition: 'easeOutQuad'
});
else
this._fadePageIn(oldPage);
},
_a11yFocusPage: function(page) {
@ -230,33 +246,41 @@ const ViewSelector = new Lang.Class({
},
_onShowAppsButtonToggled: function() {
if (this.active)
this.reset();
else
this._showPage(this._showAppsButton.checked ? this._appsPage
: this._workspacesPage);
if (this._showAppsBlocked)
return;
this._showPage(this._showAppsButton.checked ?
this._appsPage : this._workspacesPage);
},
_resetShowAppsButton: function() {
this._showAppsBlocked = true;
this._showAppsButton.checked = false;
this._showAppsBlocked = false;
this._showPage(this._workspacesPage, true);
},
_onStageKeyPress: function(actor, event) {
// Ignore events while anything but the overview has
// pushed a modal (system modals, looking glass, ...)
if (Main.modalCount > 1)
return false;
let modifiers = event.get_state();
let symbol = event.get_key_symbol();
if (symbol == Clutter.Escape) {
if (this.active)
if (this._searchActive)
this.reset();
else if (this._showAppsButton.checked)
this._resetShowAppsButton();
this._showAppsButton.checked = false;
else
Main.overview.hide();
return true;
} else if (Clutter.keysym_to_unicode(symbol) ||
(symbol == Clutter.BackSpace && this.active)) {
} else if (this._shouldTriggerSearch(symbol)) {
this.startSearch(event);
} else if (!this.active) {
} else if (!this._searchActive) {
if (symbol == Clutter.Tab || symbol == Clutter.Down) {
this._activePage.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
return true;
@ -319,6 +343,17 @@ const ViewSelector = new Lang.Class({
}
},
_shouldTriggerSearch: function(symbol) {
let unicode = Clutter.keysym_to_unicode(symbol);
if (unicode == 0)
return false;
if (getTermsForSearchString(String.fromCharCode(unicode)).length > 0)
return true;
return symbol == Clutter.BackSpace && this._searchActive;
},
startSearch: function(event) {
global.stage.set_key_focus(this._text);
this._text.event(event, true);
@ -330,39 +365,39 @@ const ViewSelector = new Lang.Class({
},
_onTextChanged: function (se, prop) {
let searchPreviouslyActive = this.active;
this.active = this._entry.get_text() != '';
this._searchPending = this.active && !searchPreviouslyActive;
if (this._searchPending) {
let terms = getTermsForSearchString(this._entry.get_text());
let searchPreviouslyActive = this._searchActive;
this._searchActive = (terms.length > 0);
let startSearch = this._searchActive && !searchPreviouslyActive;
if (startSearch)
this._searchResults.startingSearch();
}
if (this.active) {
this._entry.set_secondary_icon(this._activeIcon);
if (this._iconClickedId == 0) {
if (this._searchActive) {
this._entry.set_secondary_icon(this._clearIcon);
if (this._iconClickedId == 0)
this._iconClickedId = this._entry.connect('secondary-icon-clicked',
Lang.bind(this, function() {
this.reset();
}));
}
} else {
if (this._iconClickedId > 0)
this._entry.disconnect(this._iconClickedId);
this._iconClickedId = 0;
Lang.bind(this, this.reset));
if (this._searchTimeoutId == 0)
this._searchTimeoutId = Mainloop.timeout_add(150,
Lang.bind(this, this._doSearch));
} else {
if (this._iconClickedId > 0) {
this._entry.disconnect(this._iconClickedId);
this._iconClickedId = 0;
}
this._entry.set_secondary_icon(this._inactiveIcon);
this._searchCancelled();
}
if (!this.active) {
if (this._searchTimeoutId > 0) {
Mainloop.source_remove(this._searchTimeoutId);
this._searchTimeoutId = 0;
}
return;
this._entry.set_secondary_icon(null);
this._searchCancelled();
}
if (this._searchTimeoutId > 0)
return;
this._searchTimeoutId = Mainloop.timeout_add(150, Lang.bind(this, this._doSearch));
},
_onKeyPress: function(entry, event) {
@ -372,16 +407,7 @@ const ViewSelector = new Lang.Class({
this.reset();
return true;
}
} else if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
// We can't connect to 'activate' here because search providers
// might want to do something with the modifiers in activateDefault.
if (this._searchTimeoutId > 0) {
Mainloop.source_remove(this._searchTimeoutId);
this._doSearch();
}
this._searchResults.activateDefault();
return true;
} else if (this.active) {
} else if (this._searchActive) {
let arrowNext, nextDirection;
if (entry.get_text_direction() == Clutter.TextDirection.RTL) {
arrowNext = Clutter.Left;
@ -405,6 +431,15 @@ const ViewSelector = new Lang.Class({
} else if (symbol == arrowNext && this._text.position == -1) {
this._searchResults.navigateFocus(nextDirection);
return true;
} else if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) {
// We can't connect to 'activate' here because search providers
// might want to do something with the modifiers in activateDefault.
if (this._searchTimeoutId > 0) {
Mainloop.source_remove(this._searchTimeoutId);
this._doSearch();
}
this._searchResults.activateDefault();
return true;
}
}
return false;
@ -427,13 +462,39 @@ const ViewSelector = new Lang.Class({
_doSearch: function () {
this._searchTimeoutId = 0;
let text = this._text.get_text().replace(/^\s+/g, '').replace(/\s+$/g, '');
this._searchResults.doSearch(text);
let terms = getTermsForSearchString(this._entry.get_text());
this._searchSystem.updateSearchResults(terms);
this._showPage(this._searchPage);
},
_shouldUseSearchProvider: function(provider) {
// the disable-external GSetting only affects remote providers
if (!provider.isRemoteProvider)
return true;
if (this._searchSettings.get_boolean('disable-external'))
return false;
let appId = provider.appInfo.get_id();
let disable = this._searchSettings.get_strv('disabled');
return disable.indexOf(appId) == -1;
},
_reloadRemoteProviders: function() {
// removeSearchProvider() modifies the provider list we iterate on,
// so make a copy first
let remoteProviders = this._searchSystem.getRemoteProviders().slice(0);
remoteProviders.forEach(Lang.bind(this, this.removeSearchProvider));
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));
},
addSearchProvider: function(provider) {
if (!this._shouldUseSearchProvider(provider))
return;
this._searchSystem.registerProvider(provider);
this._searchResults.createProviderMeta(provider);
},
@ -441,6 +502,31 @@ const ViewSelector = new Lang.Class({
removeSearchProvider: function(provider) {
this._searchSystem.unregisterProvider(provider);
this._searchResults.destroyProviderMeta(provider);
},
getActivePage: function() {
if (this._activePage == this._workspacesPage)
return ViewPage.WINDOWS;
else if (this._activePage == this._appsPage)
return ViewPage.APPS;
else
return ViewPage.SEARCH;
},
fadeIn: function() {
let actor = this._activePage;
Tweener.addTween(actor, { opacity: 255,
time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME / 2,
transition: 'easeInQuad'
});
},
fadeHalf: function() {
let actor = this._activePage;
Tweener.addTween(actor, { opacity: 128,
time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME / 2,
transition: 'easeOutQuad'
});
}
});
Signals.addSignalMethods(ViewSelector.prototype);

View File

@ -9,7 +9,7 @@ const St = imports.gi.St;
const IconGrid = imports.ui.iconGrid;
const Layout = imports.ui.layout;
const Main = imports.ui.main;
const Search = imports.ui.search;
const Panel = imports.ui.panel;
// we could make these gsettings
const FISH_NAME = 'wanda';
@ -53,20 +53,9 @@ const WandaIcon = new Lang.Class({
icon_size: iconSize });
}
this._animations = St.TextureCache.get_default().load_sliced_image(this._imageFile, this._imgWidth, this._imgHeight);
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));
this._i = 0;
this._update();
} else if (!this._animations.mapped && this._timeoutId) {
GLib.source_remove(this._timeoutId);
this._timeoutId = 0;
}
}));
return this._animations;
this._animations = new Panel.Animation(this._imageFile, this._imgWidth, this._imgHeight, FISH_SPEED);
this._animations.play();
return this._animations.actor;
},
_createIconTexture: function(size) {
@ -74,28 +63,14 @@ const WandaIcon = new Lang.Class({
return;
this.parent(size);
},
_update: function() {
let n = this._animations.get_n_children();
if (n == 0) {
return true;
}
this._animations.get_child_at_index(this._i).hide();
this._i = (this._i + 1) % n;
this._animations.get_child_at_index(this._i).show();
return true;
},
}
});
const WandaIconBin = new Lang.Class({
Name: 'WandaIconBin',
_init: function(fish, label, params) {
this.actor = new St.Bin({ style_class: 'search-result-content',
reactive: true,
this.actor = new St.Bin({ reactive: true,
track_hover: true });
this.icon = new WandaIcon(fish, label, params);
@ -156,10 +131,9 @@ function capitalize(str) {
const WandaSearchProvider = new Lang.Class({
Name: 'WandaSearchProvider',
Extends: Search.SearchProvider,
_init: function() {
this.parent(_("Your favorite Easter Egg"));
this.id = 'wanda';
},
getResultMetas: function(fish, callback) {
@ -185,7 +159,7 @@ const WandaSearchProvider = new Lang.Class({
this.getInitialResultSet(terms);
},
activateResult: function(fish, params) {
activateResult: function(fish) {
if (this._dialog)
this._dialog.destroy();
this._dialog = new FortuneDialog(capitalize(fish), FISH_COMMAND);

View File

@ -39,6 +39,8 @@ const WindowAttentionHandler = new Lang.Class({
let [title, banner] = this._getTitleAndBanner(app, window);
let notification = new MessageTray.Notification(source, title, banner);
notification.setForFeedback(true);
source.notify(notification);
source.signalIDs.push(window.connect('notify::title', Lang.bind(this, function() {

View File

@ -83,6 +83,8 @@ const WindowManager = new Lang.Class({
this._animationBlockCount = 0;
this._allowedKeybindings = {};
this._switchData = null;
this._shellwm.connect('kill-switch-workspace', Lang.bind(this, this._switchWorkspaceDone));
this._shellwm.connect('kill-window-effects', Lang.bind(this, function (shellwm, actor) {
@ -99,47 +101,100 @@ const WindowManager = new Lang.Class({
this._shellwm.connect('unmaximize', Lang.bind(this, this._unmaximizeWindow));
this._shellwm.connect('map', Lang.bind(this, this._mapWindow));
this._shellwm.connect('destroy', Lang.bind(this, this._destroyWindow));
this._shellwm.connect('filter-keybinding', Lang.bind(this, this._filterKeybinding));
this._workspaceSwitcherPopup = null;
Meta.keybindings_set_custom_handler('switch-to-workspace-left',
Lang.bind(this, this._showWorkspaceSwitcher));
Meta.keybindings_set_custom_handler('switch-to-workspace-right',
Lang.bind(this, this._showWorkspaceSwitcher));
Meta.keybindings_set_custom_handler('switch-to-workspace-up',
Lang.bind(this, this._showWorkspaceSwitcher));
Meta.keybindings_set_custom_handler('switch-to-workspace-down',
Lang.bind(this, this._showWorkspaceSwitcher));
Meta.keybindings_set_custom_handler('move-to-workspace-left',
Lang.bind(this, this._showWorkspaceSwitcher));
Meta.keybindings_set_custom_handler('move-to-workspace-right',
Lang.bind(this, this._showWorkspaceSwitcher));
Meta.keybindings_set_custom_handler('move-to-workspace-up',
Lang.bind(this, this._showWorkspaceSwitcher));
Meta.keybindings_set_custom_handler('move-to-workspace-down',
Lang.bind(this, this._showWorkspaceSwitcher));
Meta.keybindings_set_custom_handler('switch-windows',
Lang.bind(this, this._startAppSwitcher));
Meta.keybindings_set_custom_handler('switch-group',
Lang.bind(this, this._startAppSwitcher));
Meta.keybindings_set_custom_handler('switch-windows-backward',
Lang.bind(this, this._startAppSwitcher));
Meta.keybindings_set_custom_handler('switch-group-backward',
Lang.bind(this, this._startAppSwitcher));
Meta.keybindings_set_custom_handler('switch-panels',
Lang.bind(this, this._startA11ySwitcher));
global.display.add_keybinding('open-application-menu',
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.NONE,
Lang.bind(this, this._openAppMenu));
this.setCustomKeybindingHandler('switch-to-workspace-left',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-right',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-up',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-to-workspace-down',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-left',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-right',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-up',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('move-to-workspace-down',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW,
Lang.bind(this, this._showWorkspaceSwitcher));
this.setCustomKeybindingHandler('switch-applications',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._startAppSwitcher));
this.setCustomKeybindingHandler('switch-group',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._startAppSwitcher));
this.setCustomKeybindingHandler('switch-applications-backward',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._startAppSwitcher));
this.setCustomKeybindingHandler('switch-group-backward',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._startAppSwitcher));
this.setCustomKeybindingHandler('switch-windows',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._startWindowSwitcher));
this.setCustomKeybindingHandler('switch-windows-backward',
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._startWindowSwitcher));
this.setCustomKeybindingHandler('switch-panels',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW |
Shell.KeyBindingMode.LOCK_SCREEN |
Shell.KeyBindingMode.UNLOCK_SCREEN |
Shell.KeyBindingMode.LOGIN_SCREEN,
Lang.bind(this, this._startA11ySwitcher));
this.setCustomKeybindingHandler('switch-panels-backward',
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW |
Shell.KeyBindingMode.LOCK_SCREEN |
Shell.KeyBindingMode.UNLOCK_SCREEN |
Shell.KeyBindingMode.LOGIN_SCREEN,
Lang.bind(this, this._startA11ySwitcher));
Main.overview.connect('showing', Lang.bind(this, function() {
for (let i = 0; i < this._dimmedWindows.length; i++)
this._undimWindow(this._dimmedWindows[i]);
}));
Main.overview.connect('hiding', Lang.bind(this, function() {
for (let i = 0; i < this._dimmedWindows.length; i++)
this._dimWindow(this._dimmedWindows[i]);
}));
this.addKeybinding('open-application-menu',
new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }),
Meta.KeyBindingFlags.NONE,
Shell.KeyBindingMode.NORMAL,
Lang.bind(this, this._openAppMenu));
},
setCustomKeybindingHandler: function(name, modes, handler) {
if (Meta.keybindings_set_custom_handler(name, handler))
this.allowKeybinding(name, modes);
},
addKeybinding: function(name, settings, flags, modes, handler) {
let action = global.display.add_keybinding(name, settings, flags, handler);
if (action != Meta.KeyBindingAction.NONE)
this.allowKeybinding(name, modes);
return action;
},
removeKeybinding: function(name) {
if (global.display.remove_keybinding(name))
this.allowKeybinding(name, Shell.KeyBindingMode.NONE);
},
allowKeybinding: function(name, modes) {
this._allowedKeybindings[name] = modes;
},
blockAnimations: function() {
@ -157,7 +212,9 @@ const WindowManager = new Lang.Class({
_shouldAnimateActor: function(actor) {
if (!this._shouldAnimate())
return false;
return actor.meta_window.get_window_type() == Meta.WindowType.NORMAL;
let windowType = actor.meta_window.get_window_type();
return windowType == Meta.WindowType.NORMAL ||
windowType == Meta.WindowType.MODAL_DIALOG;
},
_removeEffect : function(list, actor) {
@ -176,23 +233,12 @@ const WindowManager = new Lang.Class({
}
actor.set_scale(1.0, 1.0);
actor.move_anchor_point_from_gravity(Clutter.Gravity.CENTER);
/* scale window down to 0x0.
* maybe TODO: get icon geometry passed through and move the window towards it?
*/
this._minimizing.push(actor);
let primary = Main.layoutManager.primaryMonitor;
let xDest = primary.x;
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
xDest += primary.width;
Tweener.addTween(actor,
{ scale_x: 0.0,
scale_y: 0.0,
x: xDest,
y: 0,
if (actor.meta_window.is_monitor_sized()) {
Tweener.addTween(actor,
{ opacity: 0,
time: WINDOW_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._minimizeWindowDone,
@ -202,12 +248,46 @@ const WindowManager = new Lang.Class({
onOverwriteScope: this,
onOverwriteParams: [shellwm, actor]
});
} else {
let xDest, yDest, xScale, yScale;
let [success, geom] = actor.meta_window.get_icon_geometry();
if (success) {
xDest = geom.x;
yDest = geom.y;
xScale = geom.width / actor.width;
yScale = geom.height / actor.height;
} else {
let monitor = Main.layoutManager.monitors[actor.meta_window.get_monitor()];
xDest = monitor.x;
yDest = monitor.y;
if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
xDest += monitor.width;
xScale = 0;
yScale = 0;
}
Tweener.addTween(actor,
{ scale_x: xScale,
scale_y: yScale,
x: xDest,
y: yDest,
time: WINDOW_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._minimizeWindowDone,
onCompleteScope: this,
onCompleteParams: [shellwm, actor],
onOverwrite: this._minimizeWindowOverwritten,
onOverwriteScope: this,
onOverwriteParams: [shellwm, actor]
});
}
},
_minimizeWindowDone : function(shellwm, actor) {
if (this._removeEffect(this._minimizing, actor)) {
Tweener.removeTweens(actor);
actor.set_scale(1.0, 1.0);
actor.set_opacity(255);
actor.move_anchor_point_from_gravity(Clutter.Gravity.NORTH_WEST);
shellwm.completed_minimize(actor);
@ -253,15 +333,13 @@ const WindowManager = new Lang.Class({
if (shouldDim && !window._dimmed) {
window._dimmed = true;
this._dimmedWindows.push(window);
if (!Main.overview.visible)
this._dimWindow(window);
this._dimWindow(window);
} else if (!shouldDim && window._dimmed) {
window._dimmed = false;
this._dimmedWindows = this._dimmedWindows.filter(function(win) {
return win != window;
});
if (!Main.overview.visible)
this._undimWindow(window);
this._undimWindow(window);
}
},
@ -307,57 +385,56 @@ const WindowManager = new Lang.Class({
actor._windowType = type;
}));
if (actor.meta_window.is_attached_dialog()) {
this._checkDimming(actor.get_meta_window().get_transient_for());
if (this._shouldAnimate()) {
actor.set_scale(1.0, 0.0);
actor.scale_gravity = Clutter.Gravity.CENTER;
actor.show();
this._mapping.push(actor);
Tweener.addTween(actor,
{ scale_y: 1,
time: WINDOW_ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: this._mapWindowDone,
onCompleteScope: this,
onCompleteParams: [shellwm, actor],
onOverwrite: this._mapWindowOverwrite,
onOverwriteScope: this,
onOverwriteParams: [shellwm, actor]
});
return;
}
shellwm.completed_map(actor);
return;
}
if (!this._shouldAnimateActor(actor)) {
shellwm.completed_map(actor);
return;
}
actor.opacity = 0;
actor.show();
if (actor.meta_window.is_attached_dialog()) {
/* Scale the window from the center of the parent */
this._checkDimming(actor.get_meta_window().get_transient_for());
actor.set_scale(1.0, 0.0);
actor.scale_gravity = Clutter.Gravity.CENTER;
actor.show();
this._mapping.push(actor);
/* Fade window in */
this._mapping.push(actor);
Tweener.addTween(actor,
{ opacity: 255,
time: WINDOW_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._mapWindowDone,
onCompleteScope: this,
onCompleteParams: [shellwm, actor],
onOverwrite: this._mapWindowOverwrite,
onOverwriteScope: this,
onOverwriteParams: [shellwm, actor]
});
Tweener.addTween(actor,
{ scale_y: 1,
time: WINDOW_ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: this._mapWindowDone,
onCompleteScope: this,
onCompleteParams: [shellwm, actor],
onOverwrite: this._mapWindowOverwrite,
onOverwriteScope: this,
onOverwriteParams: [shellwm, actor]
});
} else {
/* Fade window in */
actor.opacity = 0;
actor.show();
this._mapping.push(actor);
Tweener.addTween(actor,
{ opacity: 255,
time: WINDOW_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._mapWindowDone,
onCompleteScope: this,
onCompleteParams: [shellwm, actor],
onOverwrite: this._mapWindowOverwrite,
onOverwriteScope: this,
onOverwriteParams: [shellwm, actor]
});
}
},
_mapWindowDone : function(shellwm, actor) {
if (this._removeEffect(this._mapping, actor)) {
Tweener.removeTweens(actor);
actor.opacity = 255;
actor.scale_y = 1;
shellwm.completed_map(actor);
}
},
@ -379,18 +456,21 @@ const WindowManager = new Lang.Class({
return win != window;
});
}
if (!this._shouldAnimateActor(actor)) {
shellwm.completed_destroy(actor);
return;
}
this._destroying.push(actor);
if (window.is_attached_dialog()) {
let parent = window.get_transient_for();
this._checkDimming(parent, window);
if (!this._shouldAnimate()) {
shellwm.completed_destroy(actor);
return;
}
actor.set_scale(1.0, 1.0);
actor.scale_gravity = Clutter.Gravity.CENTER;
actor.show();
this._destroying.push(actor);
actor._parentDestroyId = parent.connect('unmanaged', Lang.bind(this, function () {
Tweener.removeTweens(actor);
@ -424,6 +504,21 @@ const WindowManager = new Lang.Class({
}
},
_filterKeybinding: function(shellwm, binding) {
if (Main.keybindingMode == Shell.KeyBindingMode.NONE)
return true;
// There's little sense in implementing a keybinding in mutter and
// not having it work in NORMAL mode; handle this case generically
// so we don't have to explicitly allow all builtin keybindings in
// NORMAL mode.
if (Main.keybindingMode == Shell.KeyBindingMode.NORMAL &&
binding.is_builtin())
return false;
return !(this._allowedKeybindings[binding.get_name()] & Main.keybindingMode);
},
_switchWorkspace : function(shellwm, from, to, direction) {
if (!this._shouldAnimate()) {
shellwm.completed_switch_workspace();
@ -441,11 +536,11 @@ const WindowManager = new Lang.Class({
if (direction == Meta.MotionDirection.UP ||
direction == Meta.MotionDirection.UP_LEFT ||
direction == Meta.MotionDirection.UP_RIGHT)
yDest = global.screen_height;
yDest = global.screen_height - Main.panel.actor.height;
else if (direction == Meta.MotionDirection.DOWN ||
direction == Meta.MotionDirection.DOWN_LEFT ||
direction == Meta.MotionDirection.DOWN_RIGHT)
yDest = -global.screen_height;
yDest = -global.screen_height + Main.panel.actor.height;
if (direction == Meta.MotionDirection.LEFT ||
direction == Meta.MotionDirection.UP_LEFT ||
@ -458,9 +553,9 @@ const WindowManager = new Lang.Class({
let switchData = {};
this._switchData = switchData;
switchData.inGroup = new Clutter.Group();
switchData.outGroup = new Clutter.Group();
switchData.movingWindowBin = new Clutter.Group();
switchData.inGroup = new Clutter.Actor();
switchData.outGroup = new Clutter.Actor();
switchData.movingWindowBin = new Clutter.Actor();
switchData.windows = [];
let wgroup = global.window_group;
@ -487,7 +582,7 @@ const WindowManager = new Lang.Class({
switchData.windows.push({ window: window,
parent: window.get_parent() });
window.reparent(switchData.inGroup);
window.show_all();
window.show();
}
}
@ -546,7 +641,20 @@ const WindowManager = new Lang.Class({
if (this._workspaceSwitcherPopup != null)
this._workspaceSwitcherPopup.destroy();
let tabPopup = new AltTab.AltTabPopup();
let tabPopup = new AltTab.AppSwitcherPopup();
let modifiers = binding.get_modifiers();
let backwards = modifiers & Meta.VirtualModifier.SHIFT_MASK;
if (!tabPopup.show(backwards, binding.get_name(), binding.get_mask()))
tabPopup.destroy();
},
_startWindowSwitcher : function(display, screen, window, binding) {
/* prevent a corner case where both popups show up at once */
if (this._workspaceSwitcherPopup != null)
this._workspaceSwitcherPopup.destroy();
let tabPopup = new AltTab.WindowSwitcherPopup();
let modifiers = binding.get_modifiers();
let backwards = modifiers & Meta.VirtualModifier.SHIFT_MASK;
@ -557,7 +665,7 @@ const WindowManager = new Lang.Class({
_startA11ySwitcher : function(display, screen, window, binding) {
let modifiers = binding.get_modifiers();
let backwards = modifiers & Meta.VirtualModifier.SHIFT_MASK;
Main.ctrlAltTabManager.popup(backwards, binding.get_mask());
Main.ctrlAltTabManager.popup(backwards, binding.get_name(), binding.get_mask());
},
_openAppMenu : function(display, screen, window, event, binding) {

File diff suppressed because it is too large Load Diff

View File

@ -55,10 +55,9 @@ const WorkspaceSwitcherPopup = new Lang.Class({
_getPreferredHeight : function (actor, forWidth, alloc) {
let children = this._list.get_children();
let primary = Main.layoutManager.primaryMonitor;
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
let availHeight = primary.height;
availHeight -= Main.panel.actor.height;
let availHeight = workArea.height;
availHeight -= this.actor.get_theme_node().get_vertical_padding();
availHeight -= this._container.get_theme_node().get_vertical_padding();
availHeight -= this._list.get_theme_node().get_vertical_padding();
@ -67,7 +66,7 @@ const WorkspaceSwitcherPopup = new Lang.Class({
for (let i = 0; i < children.length; i++) {
let [childMinHeight, childNaturalHeight] = children[i].get_preferred_height(-1);
let [childMinWidth, childNaturalWidth] = children[i].get_preferred_width(childNaturalHeight);
height += childNaturalHeight * primary.width / primary.height;
height += childNaturalHeight * workArea.width / workArea.height;
}
let spacing = this._itemSpacing * (global.screen.n_workspaces - 1);
@ -81,8 +80,8 @@ const WorkspaceSwitcherPopup = new Lang.Class({
},
_getPreferredWidth : function (actor, forHeight, alloc) {
let primary = Main.layoutManager.primaryMonitor;
this._childWidth = Math.round(this._childHeight * primary.width / primary.height);
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
this._childWidth = Math.round(this._childHeight * workArea.width / workArea.height);
alloc.min_size = this._childWidth;
alloc.natural_size = this._childWidth;
@ -122,12 +121,11 @@ const WorkspaceSwitcherPopup = new Lang.Class({
}
let primary = Main.layoutManager.primaryMonitor;
let workArea = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
let [containerMinHeight, containerNatHeight] = this._container.get_preferred_height(global.screen_width);
let [containerMinWidth, containerNatWidth] = this._container.get_preferred_width(containerNatHeight);
this._container.x = primary.x + Math.floor((primary.width - containerNatWidth) / 2);
this._container.y = primary.y + Main.panel.actor.height +
Math.floor(((primary.height - Main.panel.actor.height) - containerNatHeight) / 2);
this._container.x = workArea.x + Math.floor((workArea.width - containerNatWidth) / 2);
this._container.y = workArea.y + Math.floor((workArea.height - containerNatHeight) / 2);
},
_show : function() {

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 Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@ -8,9 +9,11 @@ const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const Background = imports.ui.background;
const DND = imports.ui.dnd;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const WindowManager = imports.ui.windowManager;
const Workspace = imports.ui.workspace;
const WorkspacesView = imports.ui.workspacesView;
@ -27,20 +30,51 @@ const WORKSPACE_CUT_SIZE = 10;
const WORKSPACE_KEEP_ALIVE_TIME = 100;
const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides';
/* A layout manager that requests size only for primary_actor, but then allocates
all using a fixed layout */
const PrimaryActorLayout = new Lang.Class({
Name: 'PrimaryActorLayout',
Extends: Clutter.FixedLayout,
_init: function(primaryActor) {
this.parent();
this.primaryActor = primaryActor;
},
vfunc_get_preferred_width: function(forHeight) {
return this.primaryActor.get_preferred_width(forHeight);
},
vfunc_get_preferred_height: function(forWidth) {
return this.primaryActor.get_preferred_height(forWidth);
},
});
const WindowClone = new Lang.Class({
Name: 'WindowClone',
_init : function(realWindow) {
this.actor = new Clutter.Clone({ source: realWindow.get_texture(),
this.clone = new Clutter.Clone({ source: realWindow });
/* Can't use a Shell.GenericContainer because of DND and reparenting... */
this.actor = new Clutter.Actor({ layout_manager: new PrimaryActorLayout(this.clone),
reactive: true });
this.actor._delegate = this;
this.actor.add_child(this.clone);
this.realWindow = realWindow;
this.metaWindow = realWindow.meta_window;
this._positionChangedId = this.realWindow.connect('position-changed',
Lang.bind(this, this._onPositionChanged));
this._realWindowDestroyedId = this.realWindow.connect('destroy',
Lang.bind(this, this._disconnectRealWindowSignals));
this.clone._updateId = this.realWindow.connect('position-changed',
Lang.bind(this, this._onPositionChanged));
this.clone._destroyId = this.realWindow.connect('destroy', Lang.bind(this, function() {
// First destroy the clone and then destroy everything
// This will ensure that we never see it in the _disconnectSignals loop
this.clone.destroy();
this.destroy();
}));
this._onPositionChanged();
this.actor.connect('button-release-event',
@ -56,6 +90,24 @@ const WindowClone = new Lang.Class({
this._draggable.connect('drag-cancelled', Lang.bind(this, this._onDragCancelled));
this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd));
this.inDrag = false;
let iter = Lang.bind(this, function(win) {
let actor = win.get_compositor_private();
if (!actor)
return false;
if (!win.is_attached_dialog())
return false;
this._doAddAttachedDialog(win, actor);
win.foreach_transient(iter);
return true;
});
this.metaWindow.foreach_transient(iter);
this._dimmer = new WindowManager.WindowDimmer(this.clone);
this._updateDimmer();
},
setStackAbove: function (actor) {
@ -70,25 +122,57 @@ const WindowClone = new Lang.Class({
this.actor.destroy();
},
addAttachedDialog: function(win) {
this._doAddAttachedDialog(win, win.get_compositor_private());
this._updateDimmer();
},
_doAddAttachedDialog: function(metaDialog, realDialog) {
let clone = new Clutter.Clone({ source: realDialog });
this._updateDialogPosition(realDialog, clone);
clone._updateId = realDialog.connect('position-changed',
Lang.bind(this, this._updateDialogPosition, clone));
clone._destroyId = realDialog.connect('destroy', Lang.bind(this, function() {
clone.destroy();
this._updateDimmer();
}));
this.actor.add_child(clone);
},
_updateDimmer: function() {
if (this.actor.get_n_children() > 1) {
this._dimmer.setEnabled(true);
this._dimmer.dimFactor = 1.0;
} else {
this._dimmer.setEnabled(false);
}
},
_updateDialogPosition: function(realDialog, cloneDialog) {
let metaDialog = realDialog.meta_window;
let dialogRect = metaDialog.get_outer_rect();
let rect = this.metaWindow.get_outer_rect();
cloneDialog.set_position(dialogRect.x - rect.x, dialogRect.y - rect.y);
},
_onPositionChanged: function() {
let rect = this.metaWindow.get_outer_rect();
this.actor.set_position(this.realWindow.x, this.realWindow.y);
},
_disconnectRealWindowSignals: function() {
if (this._positionChangedId != 0) {
this.realWindow.disconnect(this._positionChangedId);
this._positionChangedId = 0;
}
_disconnectSignals: function() {
this.actor.get_children().forEach(function(child) {
let realWindow = child.source;
if (this._realWindowDestroyedId != 0) {
this.realWindow.disconnect(this._realWindowDestroyedId);
this._realWindowDestroyedId = 0;
}
realWindow.disconnect(child._updateId);
realWindow.disconnect(child._destroyId);
});
},
_onDestroy: function() {
this._disconnectRealWindowSignals();
this._disconnectSignals();
this.actor._delegate = null;
@ -162,18 +246,20 @@ const WorkspaceThumbnail = new Lang.Class({
style_class: 'workspace-thumbnail' });
this.actor._delegate = this;
this._contents = new Clutter.Group();
this.actor.add_actor(this._contents);
this._contents = new Clutter.Actor();
this.actor.add_child(this._contents);
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
this._contents.add_actor(this._background);
this._createBackground();
let monitor = Main.layoutManager.primaryMonitor;
this.setPorthole(monitor.x, monitor.y, monitor.width, monitor.height);
let windows = global.get_window_actors().filter(this._isWorkspaceWindow, this);
let windows = global.get_window_actors().filter(Lang.bind(this, function(actor) {
let win = actor.meta_window;
return win.located_on_workspace(metaWorkspace);
}));
// Create clones for windows that should be visible in the Overview
this._windows = [];
@ -207,6 +293,12 @@ const WorkspaceThumbnail = new Lang.Class({
this._collapseFraction = 0; // Not collapsed
},
_createBackground: function() {
this._bgManager = new Background.BackgroundManager({ monitorIndex: Main.layoutManager.primaryIndex,
container: this._contents,
effects: Meta.BackgroundEffects.NONE });
},
setPorthole: function(x, y, width, height) {
this._portholeX = x;
this._portholeY = y;
@ -230,7 +322,7 @@ const WorkspaceThumbnail = new Lang.Class({
let clone = this._windows[i];
let metaWindow = clone.metaWindow;
if (i == 0) {
clone.setStackAbove(this._background);
clone.setStackAbove(this._bgManager.background.actor);
} else {
let previousClone = this._windows[i - 1];
clone.setStackAbove(previousClone.actor);
@ -308,10 +400,26 @@ const WorkspaceThumbnail = new Lang.Class({
if (this._lookupIndex (metaWin) != -1)
return;
if (!this._isMyWindow(win) || !this._isOverviewWindow(win))
if (!this._isMyWindow(win))
return;
let clone = this._addWindowClone(win);
if (this._isOverviewWindow(win)) {
this._addWindowClone(win);
} else if (metaWin.is_attached_dialog()) {
let parent = metaWin.get_transient_for();
while (parent.is_attached_dialog())
parent = metaWin.get_transient_for();
let idx = this._lookupIndex (parent);
if (idx < 0) {
// parent was not created yet, it will take care
// of the dialog when created
return;
}
let clone = this._windows[idx];
clone.addAttachedDialog(metaWin);
}
},
_windowAdded : function(metaWorkspace, metaWin) {
@ -350,6 +458,8 @@ const WorkspaceThumbnail = new Lang.Class({
destroy : function() {
this.actor.destroy();
this._bgManager.destroy();
this._bgManager = null;
},
workspaceRemoved : function() {
@ -374,15 +484,11 @@ const WorkspaceThumbnail = new Lang.Class({
this.actor = null;
},
// Tests if @win belongs to this workspace
_isWorkspaceWindow : function (win) {
return Main.isWindowActorDisplayedOnWorkspace(win, this.metaWorkspace.index());
},
// Tests if @win belongs to this workspace and monitor
_isMyWindow : function (win) {
return this._isWorkspaceWindow(win) &&
(!win.get_meta_window() || win.get_meta_window().get_monitor() == this.monitorIndex);
// Tests if @actor belongs to this workspace and monitor
_isMyWindow : function (actor) {
let win = actor.meta_window;
return win.located_on_workspace(this.metaWorkspace) &&
(win.get_monitor() == this.monitorIndex);
},
// Tests if @win should be shown in the Overview
@ -415,7 +521,7 @@ const WorkspaceThumbnail = new Lang.Class({
this._contents.add_actor(clone.actor);
if (this._windows.length == 0)
clone.setStackAbove(this._background);
clone.setStackAbove(this._bgManager.background.actor);
else
clone.setStackAbove(this._windows[this._windows.length - 1].actor);
@ -542,6 +648,11 @@ 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));
Main.overview.connect('showing',
Lang.bind(this, this._createThumbnails));
Main.overview.connect('hidden',
Lang.bind(this, this._destroyThumbnails));
Main.overview.connect('item-drag-begin',
Lang.bind(this, this._onDragBegin));
Main.overview.connect('item-drag-end',
@ -554,6 +665,16 @@ const ThumbnailsBox = new Lang.Class({
Lang.bind(this, this._onDragEnd));
Main.overview.connect('window-drag-cancelled',
Lang.bind(this, this._onDragCancelled));
this._settings = new Gio.Settings({ schema: OVERRIDE_SCHEMA });
this._settings.connect('changed::dynamic-workspaces',
Lang.bind(this, this._updateSwitcherVisibility));
},
_updateSwitcherVisibility: function() {
this.actor.visible =
this._settings.get_boolean('dynamic-workspaces') ||
global.screen.n_workspaces > 1;
},
_onButtonRelease: function(actor, event) {
@ -564,7 +685,7 @@ const ThumbnailsBox = new Lang.Class({
let thumbnail = this._thumbnails[i]
let [w, h] = thumbnail.actor.get_transformed_size();
if (y >= thumbnail.actor.y && y <= thumbnail.actor.y + h) {
thumbnail.activate(event.time);
thumbnail.activate(event.get_time());
break;
}
}
@ -719,10 +840,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('windows-restacked',
Lang.bind(this, this._syncStacking));
this._targetScale = 0;
this._scale = 0;
@ -734,29 +861,57 @@ const ThumbnailsBox = new Lang.Class({
this._stateCounts[ThumbnailState[key]] = 0;
// The "porthole" is the portion of the screen that we show in the workspaces
let panelHeight = Main.panel.actor.height;
let monitor = Main.layoutManager.primaryMonitor;
this._porthole = {
x: monitor.x,
y: monitor.y + panelHeight,
width: monitor.width,
height: monitor.height - panelHeight
};
this._porthole = Main.layoutManager.getWorkAreaForMonitor(Main.layoutManager.primaryIndex);
this.addThumbnails(0, global.screen.n_workspaces);
this._updateSwitcherVisibility();
},
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 = [];
},
_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);
}
this._updateSwitcherVisibility();
},
addThumbnails: function(start, count) {
for (let k = start; k < start + count; k++) {
let metaWorkspace = global.screen.get_workspace_by_index(k);
@ -802,7 +957,7 @@ const ThumbnailsBox = new Lang.Class({
this._queueUpdateStates();
},
syncStacking: function(stackIndices) {
_syncStacking: function(overview, stackIndices) {
for (let i = 0; i < this._thumbnails.length; i++)
this._thumbnails[i].syncStacking(stackIndices);
},
@ -940,8 +1095,6 @@ const ThumbnailsBox = new Lang.Class({
// See comment about this._background in _init()
let themeNode = this._background.get_theme_node();
forWidth = themeNode.adjust_for_width(forWidth);
// Note that for getPreferredWidth/Height we cheat a bit and skip propagating
// the size request to our children because we know how big they are and know
// that the actors aren't depending on the virtual functions being called.

View File

@ -2,6 +2,7 @@
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GObject = imports.gi.GObject;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@ -22,14 +23,13 @@ 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',
_init: function(workspaces) {
this.actor = new St.Widget({ style_class: 'workspaces-view' });
this.actor = new St.Widget({ style_class: 'workspaces-view',
reactive: true });
// The actor itself isn't a drop target, so we don't want to pick on its area
this.actor.set_size(0, 0);
@ -47,10 +47,6 @@ const WorkspacesView = new Lang.Class({
this._height = 0;
this._x = 0;
this._y = 0;
this._clipX = 0;
this._clipY = 0;
this._clipWidth = 0;
this._clipHeight = 0;
this._spacing = 0;
this._animating = false; // tweening
this._scrolling = false; // swipe-scrolling
@ -68,7 +64,7 @@ const WorkspacesView = new Lang.Class({
// Add workspace actors
for (let w = 0; w < global.screen.n_workspaces; w++)
this._workspaces[w].actor.reparent(this.actor);
this.actor.add_actor(this._workspaces[w].actor);
this._workspaces[activeWorkspaceIndex].actor.raise_top();
this._extraWorkspaces = [];
@ -89,8 +85,8 @@ const WorkspacesView = new Lang.Class({
this._overviewShownId =
Main.overview.connect('shown',
Lang.bind(this, function() {
this.actor.set_clip(this._clipX, this._clipY,
this._clipWidth, this._clipHeight);
this.actor.set_clip(this._x, this._y,
this._width, this._height);
}));
this.scrollAdjustment = new St.Adjustment({ value: activeWorkspaceIndex,
@ -128,8 +124,10 @@ const WorkspacesView = new Lang.Class({
continue;
let ws = new Workspace.Workspace(null, i);
ws.setGeometry(monitors[i].x, monitors[i].y,
monitors[i].width, monitors[i].height);
ws.setGeometry(monitors[i].x,
monitors[i].y,
monitors[i].width,
monitors[i].height);
global.overlay_group.add_actor(ws.actor);
this._extraWorkspaces.push(ws);
}
@ -155,13 +153,6 @@ const WorkspacesView = new Lang.Class({
this._workspaces[i].setGeometry(x, y, width, height);
},
setClipRect: function(x, y, width, height) {
this._clipX = x;
this._clipY = y;
this._clipWidth = width;
this._clipHeight = height;
},
_lookupWorkspaceForMetaWindow: function (metaWindow) {
for (let i = 0; i < this._workspaces.length; i++) {
if (this._workspaces[i].containsMetaWindow(metaWindow))
@ -200,16 +191,11 @@ const WorkspacesView = new Lang.Class({
this._extraWorkspaces[i].syncStacking(stackIndices);
},
updateWindowPositions: function() {
for (let w = 0; w < this._workspaces.length; w++)
this._workspaces[w].positionWindows(Workspace.WindowPositionFlags.ANIMATE);
},
_scrollToActive: function(showAnimation) {
_scrollToActive: function() {
let active = global.screen.get_active_workspace_index();
this._updateWorkspaceActors(showAnimation);
this._updateScrollAdjustment(active, showAnimation);
this._updateWorkspaceActors(true);
this._updateScrollAdjustment(active);
},
// Update workspace actors parameters
@ -267,26 +253,21 @@ const WorkspacesView = new Lang.Class({
}
},
_updateScrollAdjustment: function(index, showAnimation) {
_updateScrollAdjustment: function(index) {
if (this._scrolling)
return;
this._animatingScroll = true;
if (showAnimation) {
Tweener.addTween(this.scrollAdjustment, {
value: index,
time: WORKSPACE_SWITCH_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
this._animatingScroll = false;
})
});
} else {
this.scrollAdjustment.value = index;
this._animatingScroll = false;
}
Tweener.addTween(this.scrollAdjustment, {
value: index,
time: WORKSPACE_SWITCH_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
this._animatingScroll = false;
})
});
},
updateWorkspaces: function(oldNumWorkspaces, newNumWorkspaces) {
@ -313,7 +294,7 @@ const WorkspacesView = new Lang.Class({
if (this._scrolling)
return;
this._scrollToActive(true);
this._scrollToActive();
},
_onDestroy: function() {
@ -387,24 +368,11 @@ const WorkspacesView = new Lang.Class({
this._scrolling = true;
},
endSwipeScroll: function(result) {
endSwipeScroll: function() {
this._scrolling = false;
if (result == Overview.SwipeScrollResult.CLICK) {
let [x, y, mod] = global.get_pointer();
let actor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL,
x, y);
// Only switch to the workspace when there's no application
// windows open. The problem is that it's too easy to miss
// an app window and get the wrong one focused.
let active = global.screen.get_active_workspace_index();
if (this._workspaces[active].isEmpty() &&
this.actor.contains(actor))
Main.overview.hide();
}
// Make sure title captions etc are shown as necessary
this._scrollToActive();
this._updateVisibility();
},
@ -452,34 +420,46 @@ const WorkspacesDisplay = new Lang.Class({
Name: 'WorkspacesDisplay',
_init: function() {
this.actor = new Shell.GenericContainer();
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate));
this.actor.connect('notify::mapped', Lang.bind(this, this._setupSwipeScrolling));
this.actor = new St.Widget({ clip_to_allocation: true });
this.actor.connect('notify::allocation', Lang.bind(this, this._updateWorkspacesGeometry));
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);
let clickAction = new Clutter.ClickAction()
clickAction.connect('clicked', Lang.bind(this, function(action) {
// Only switch to the workspace when there's no application
// windows open. The problem is that it's too easy to miss
// an app window and get the wrong one focused.
if (action.get_button() == 1 &&
this._getPrimaryView().getActiveWorkspace().isEmpty())
Main.overview.hide();
}));
Main.overview.addAction(clickAction);
this.actor.bind_property('mapped', clickAction, 'enabled', GObject.BindingFlags.SYNC_CREATE);
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));
let panAction = new Clutter.PanAction();
panAction.connect('pan', Lang.bind(this, this._onPan));
panAction.connect('gesture-begin', Lang.bind(this, function() {
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].startSwipeScroll();
return true;
}));
panAction.connect('gesture-cancel', Lang.bind(this, function() {
clickAction.release();
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].endSwipeScroll();
}));
panAction.connect('gesture-end', Lang.bind(this, function() {
clickAction.release();
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].endSwipeScroll();
}));
Main.overview.addAction(panAction);
this.actor.bind_property('mapped', panAction, 'enabled', GObject.BindingFlags.SYNC_CREATE);
this._primaryIndex = Main.layoutManager.primaryIndex;
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
controls.add_actor(this._thumbnailsBox.actor);
this._workspacesViews = null;
this._workspacesViews = [];
this._workspaces = [];
this._primaryScrollAdjustment = null;
this._settings = new Gio.Settings({ schema: OVERRIDE_SCHEMA });
@ -488,103 +468,30 @@ const WorkspacesDisplay = new Lang.Class({
this._workspacesOnlyOnPrimaryChanged));
this._workspacesOnlyOnPrimaryChanged();
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._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));
this._scrollEventId = 0;
},
_updateSwitcherVisibility: function() {
this._thumbnailsBox.actor.visible =
this._settings.get_boolean('dynamic-workspaces') ||
global.screen.n_workspaces > 1;
_onPan: function(action) {
let [dist, dx, dy] = action.get_motion_delta(0);
let adjustment = this._scrollAdjustment;
adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
return false;
},
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._updateSwitcherVisibility();
this._updateWorkspacesViews();
this._restackedNotifyId =
global.screen.connect('restacked',
Main.overview.connect('windows-restacked',
Lang.bind(this, this._onRestacked));
if (this._itemDragBeginId == 0)
this._itemDragBeginId = Main.overview.connect('item-drag-begin',
Lang.bind(this, this._dragBegin));
if (this._itemDragCancelledId == 0)
this._itemDragCancelledId = Main.overview.connect('item-drag-cancelled',
Lang.bind(this, this._dragCancelled));
if (this._itemDragEndId == 0)
this._itemDragEndId = Main.overview.connect('item-drag-end',
Lang.bind(this, this._dragEnd));
if (this._windowDragBeginId == 0)
this._windowDragBeginId = Main.overview.connect('window-drag-begin',
Lang.bind(this, this._dragBegin));
if (this._windowDragCancelledId == 0)
this._windowDragCancelledId = Main.overview.connect('window-drag-cancelled',
Lang.bind(this, this._dragCancelled));
if (this._windowDragEndId == 0)
this._windowDragEndId = Main.overview.connect('window-drag-end',
Lang.bind(this, this._dragEnd));
this._onRestacked();
if (this._scrollEventId == 0)
this._scrollEventId = Main.overview.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
},
zoomFromOverview: function() {
@ -594,44 +501,18 @@ 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._itemDragCancelledId > 0) {
Main.overview.disconnect(this._itemDragCancelledId);
this._itemDragCancelledId = 0;
}
if (this._itemDragEndId > 0) {
Main.overview.disconnect(this._itemDragEndId);
this._itemDragEndId = 0;
}
if (this._windowDragBeginId > 0) {
Main.overview.disconnect(this._windowDragBeginId);
this._windowDragBeginId = 0;
}
if (this._windowDragCancelledId > 0) {
Main.overview.disconnect(this._windowDragCancelledId);
this._windowDragCancelledId = 0;
}
if (this._windowDragEndId > 0) {
Main.overview.disconnect(this._windowDragEndId);
this._windowDragEndId = 0;
if (this._scrollEventId > 0) {
Main.overview.disconnect(this._scrollEventId);
this._scrollEventId = 0;
}
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].destroy();
this._workspacesViews = null;
this._workspacesViews = [];
for (let i = 0; i < this._workspaces.length; i++)
for (let w = 0; w < this._workspaces[i].length; w++) {
@ -640,33 +521,6 @@ const WorkspacesDisplay = new Lang.Class({
}
},
_setupSwipeScrolling: function() {
if (this._swipeScrollBeginId)
Main.overview.disconnect(this._swipeScrollBeginId);
this._swipeScrollBeginId = 0;
if (this._swipeScrollEndId)
Main.overview.disconnect(this._swipeScrollEndId);
this._swipeScrollEndId = 0;
if (!this.actor.mapped)
return;
let direction = Overview.SwipeScrollDirection.VERTICAL;
Main.overview.setScrollAdjustment(this._scrollAdjustment,
direction);
this._swipeScrollBeginId = Main.overview.connect('swipe-scroll-begin',
Lang.bind(this, function() {
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].startSwipeScroll();
}));
this._swipeScrollEndId = Main.overview.connect('swipe-scroll-end',
Lang.bind(this, function(overview, result) {
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].endSwipeScroll(result);
}));
},
_workspacesOnlyOnPrimaryChanged: function() {
this._workspacesOnlyOnPrimary = this._settings.get_boolean('workspaces-only-on-primary');
@ -677,14 +531,12 @@ const WorkspacesDisplay = new Lang.Class({
},
_updateWorkspacesViews: function() {
if (this._workspacesViews)
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].destroy();
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].destroy();
if (this._workspaces)
for (let i = 0; i < this._workspaces.length; i++)
for (let w = 0; w < this._workspaces[i].length; w++)
this._workspaces[i][w].destroy();
for (let i = 0; i < this._workspaces.length; i++)
for (let w = 0; w < this._workspaces[i].length; w++)
this._workspaces[i][w].destroy();
this._workspacesViews = [];
this._workspaces = [];
@ -702,11 +554,11 @@ const WorkspacesDisplay = new Lang.Class({
this._workspaces.push(monitorWorkspaces);
let view = new WorkspacesView(monitorWorkspaces);
view.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
if (this._workspacesOnlyOnPrimary || i == this._primaryIndex) {
this._scrollAdjustment = view.scrollAdjustment;
this._scrollAdjustment.connect('notify::value',
Lang.bind(this, this._scrollValueChanged));
this._setupSwipeScrolling();
}
this._workspacesViews.push(view);
}
@ -733,7 +585,7 @@ const WorkspacesDisplay = new Lang.Class({
},
_getPrimaryView: function() {
if (!this._workspacesViews)
if (!this._workspacesViews.length)
return null;
if (this._workspacesOnlyOnPrimary)
return this._workspacesViews[0];
@ -745,76 +597,6 @@ const WorkspacesDisplay = new Lang.Class({
return this._getPrimaryView().getActiveWorkspace().hasMaximizedWindows();
},
// zoomFraction property allows us to tween the controls sliding in and out
set zoomFraction(fraction) {
this._zoomFraction = fraction;
this.actor.queue_relayout();
},
get zoomFraction() {
return this._zoomFraction;
},
_updateAlwaysZoom: function() {
// Always show the pager if workspaces are actually used,
// e.g. there are windows on more than one
this._alwaysZoomOut = global.screen.n_workspaces > 2;
if (this._alwaysZoomOut)
return;
let monitors = Main.layoutManager.monitors;
let primary = Main.layoutManager.primaryMonitor;
/* Look for any monitor to the right of the primary, if there is
* one, we always keep zoom out, otherwise its hard to reach
* the thumbnail area without passing into the next monitor. */
for (let i = 0; i < monitors.length; i++) {
if (monitors[i].x >= primary.x + primary.width) {
this._alwaysZoomOut = true;
break;
}
}
},
_getPreferredWidth: function (actor, forHeight, alloc) {
// pass through the call in case the child needs it, but report 0x0
this._controls.get_preferred_width(forHeight);
},
_getPreferredHeight: function (actor, forWidth, alloc) {
// pass through the call in case the child needs it, but report 0x0
this._controls.get_preferred_height(forWidth);
},
_allocate: function (actor, box, flags) {
let childBox = new Clutter.ActorBox();
let totalWidth = box.x2 - box.x1;
// width of the controls
let [controlsMin, controlsNatural] = this._controls.get_preferred_width(box.y2 - box.y1);
// Amount of space on the screen we reserve for the visible control
let controlsVisible = this._controls.get_theme_node().get_length('visible-width');
let controlsReserved = controlsVisible * (1 - this._zoomFraction) + controlsNatural * this._zoomFraction;
let rtl = (Clutter.get_default_text_direction () == Clutter.TextDirection.RTL);
if (rtl) {
childBox.x2 = controlsReserved;
childBox.x1 = childBox.x2 - controlsNatural;
} else {
childBox.x1 = totalWidth - controlsReserved;
childBox.x2 = childBox.x1 + controlsNatural;
}
childBox.y1 = 0;
childBox.y2 = box.y2- box.y1;
this._controls.allocate(childBox, flags);
this._updateWorkspacesGeometry();
},
_parentSet: function(actor, oldParent) {
if (oldParent && this._notifyOpacityId)
oldParent.disconnect(this._notifyOpacityId);
@ -842,7 +624,7 @@ const WorkspacesDisplay = new Lang.Class({
},
_updateWorkspacesGeometry: function() {
if (!this._workspacesViews)
if (!this._workspacesViews.length)
return;
let fullWidth = this.actor.allocation.x2 - this.actor.allocation.x1;
@ -851,37 +633,17 @@ 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;
let clipX = rtl ? x + controlsVisible : x;
let clipY = y + (fullHeight - clipHeight) / 2;
let widthAdjust = this._zoomOut ? controlsNatural : controlsVisible;
widthAdjust += Main.overview._spacing;
width -= widthAdjust;
if (rtl)
x += widthAdjust;
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);
m++;
} else if (!this._workspacesOnlyOnPrimary) {
this._workspacesViews[m].setClipRect(monitors[i].x,
monitors[i].y,
monitors[i].width,
monitors[i].height);
this._workspacesViews[m].setGeometry(monitors[i].x,
monitors[i].y,
monitors[i].width,
@ -891,26 +653,13 @@ const WorkspacesDisplay = new Lang.Class({
}
},
_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(overview, 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)
if (!this._workspacesViews.length)
return;
let oldNumWorkspaces = this._workspaces[0].length;
@ -934,8 +683,6 @@ const WorkspacesDisplay = new Lang.Class({
}
m++;
}
this._thumbnailsBox.addThumbnails(oldNumWorkspaces, newNumWorkspaces - oldNumWorkspaces);
} else {
// Assume workspaces are only removed sequentially
// (e.g. 2,3,4 - not 2,4,7)
@ -958,87 +705,25 @@ const WorkspacesDisplay = new Lang.Class({
lostWorkspaces[l].destroy();
}
}
this._thumbnailsBox.removeThumbnails(removedIndex, removedNum);
}
for (let i = 0; i < this._workspacesViews.length; i++)
this._workspacesViews[i].updateWorkspaces(oldNumWorkspaces,
newNumWorkspaces);
this._updateSwitcherVisibility();
},
_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();
},
_dragBegin: function() {
this._inDrag = true;
this._cancelledDrag = false;
this._dragMonitor = {
dragMotion: Lang.bind(this, this._onDragMotion)
};
DND.addDragMonitor(this._dragMonitor);
},
_dragCancelled: function() {
this._cancelledDrag = true;
DND.removeDragMonitor(this._dragMonitor);
},
_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() ) {
_onScrollEvent: function(actor, event) {
if (!this.actor.mapped)
return false;
switch (event.get_scroll_direction()) {
case Clutter.ScrollDirection.UP:
Main.wm.actionMoveWorkspace(Meta.MotionDirection.UP);
break;
return true;
case Clutter.ScrollDirection.DOWN:
Main.wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
break;
return true;
}
return false;
}
});
Signals.addSignalMethods(WorkspacesDisplay.prototype);

View File

@ -2,6 +2,7 @@
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Main = imports.ui.main;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const DND = imports.ui.dnd;
@ -16,18 +17,11 @@ const XdndHandler = new Lang.Class({
// Used as a drag actor in case we don't have a cursor window clone
this._dummy = new Clutter.Rectangle({ width: 1, height: 1, opacity: 0 });
global.stage.add_actor(this._dummy);
Main.uiGroup.add_actor(this._dummy);
Shell.util_set_hidden_from_pick(this._dummy, true);
this._dummy.hide();
// Mutter delays the creation of the output window as long
// as possible to avoid flicker. In case a plugin wants to
// access it directly it has to connect to the stage's show
// signal. (see comment in compositor.c:meta_compositor_manage_screen)
global.stage.connect('show', function () {
global.init_xdnd();
return false;
});
global.init_xdnd();
global.connect('xdnd-enter', Lang.bind(this, this._onEnter));
global.connect('xdnd-position-changed', Lang.bind(this, this._onPositionChanged));
@ -74,7 +68,7 @@ const XdndHandler = new Lang.Class({
source: cursorWindow});
this._cursorWindowClone = new Clutter.Clone({ source: cursorWindow });
global.overlay_group.add_actor(this._cursorWindowClone);
Main.uiGroup.add_actor(this._cursorWindowClone);
Shell.util_set_hidden_from_pick(this._cursorWindowClone, true);
// Make sure that the clone has the same position as the source
@ -88,7 +82,7 @@ const XdndHandler = new Lang.Class({
},
_onPositionChanged: function(obj, x, y) {
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.ALL, x, y);
let pickedActor = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y);
// Make sure that the cursor window is on top
if (this._cursorWindowClone)

View File

@ -1 +1,19 @@
dist_man_MANS = gnome-shell.1
XSLTPROC_FLAGS = \
--nonet \
--stringparam man.output.quietly 1 \
--stringparam funcsynopsis.style ansi \
--stringparam man.th.extra1.suppress 1 \
--stringparam man.authors.section.enabled 0 \
--stringparam man.copyright.section.enabled 0
.xml.1:
$(AM_V_GEN) $(XSLTPROC) $(XSLTPROC_FLAGS) http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl $<
man_MANS = \
gnome-shell.1
xml_files = $(man_MANS:.1=.xml)
EXTRA_DIST = $(xml_files)
DISTCLEANFILES = $(man_MANS)

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