Compare commits

..

919 Commits

Author SHA1 Message Date
bf374ccabb nn 2014-06-03 13:51:59 -04:00
02060d2bbc nnn 2014-06-03 13:51:58 -04:00
8750f1edc0 drawer 2014-06-03 13:51:58 -04:00
798f17a97d kill tray 2014-06-03 13:51:57 -04:00
a4091adbf2 system tray elsewhere 2014-06-03 13:51:57 -04:00
ecf795b6ef reveal 2014-06-03 13:51:56 -04:00
75447b249c adjust timeout animation 2014-06-03 13:51:56 -04:00
26d2fb8a37 messageTray: Add a new message tray indicator 2014-06-03 13:51:55 -04:00
f7223763d2 messageTray: Add an indicatorCount property to the tray
So we don't have to do the property tracking here...
2014-06-03 13:51:55 -04:00
0b414308fb telepathyClient: Use a revealer for new messages in Telepathy as well 2014-06-03 13:51:55 -04:00
060917ae2b messageTray: Add a Revealer widget and use it to animate the notification in/out 2014-06-03 13:51:53 -04:00
9f2e5b9b51 messageTray: Glue the notification to the bottom of the screen, always
We'll animate notifications popping up with another system soon enough,
instead. The idea here is that instead of carefully animating the Y
position of the notificationWidget when a notification updates, we
simply animate the height of the new actor inside the notification.
This will fix some of the awkward updates where instead of the
notification content expanding, we see the buttons or action area
pushed off the edge of the screen...

Animations that happen as a result of adding something new to the
notification or expanding it should be done by tweeing the new actors
in inside the notification.
2014-06-03 13:30:46 -04:00
4a07eb77f6 messageTray: Restyle notifications 2014-06-03 13:30:45 -04:00
fbe379c81c messageTray: Considerably rework how layout is done in notifications
Use a series of nested BoxLayouts, Bins, and more instead of an StTable
and a custom ShellGenericContainer, and hacky style classes.

This also removes the customContent parameter in favor of subclasses
setting the child of this._bodyBin instead.

We lose a few of the fancy features like showing the first part of
the body, ellipsized, next the banner when it will fit, and other
layout logic. But since the layout of notifications is changing
substantially anyways, I don't feel too bad...
2014-06-03 13:30:16 -04:00
a07e8bbf37 messageTray: Remove addBody as a public API
As it's unused.
2014-06-03 13:30:16 -04:00
a6aabb1d3a messageTray: Remove support for notifications with images
This sufficiently complicates the code, and won't fit in the
new design.
2014-06-03 13:30:15 -04:00
aef6273fed messageTray: Remove support for resident notifications
Now the only resident notification is a chat notification. The convenient
thing about this special-case behavior is that there's already special-case
code for it and the shell, and we always know that a chat notification will
always be 1:1 with its chat source.
2014-06-03 13:30:15 -04:00
be8b1c7d2d notificationDaemon: Remove support for resident notifications
They're not really an API that has caught on, and not really one
we want to support, either.
2014-06-03 13:30:15 -04:00
14bc748cea notificationDaemon: Remove the special-case hack for system tray icons
Nothing in the system actually has a standard system tray icon anymore,
so this hack isn't necessary.
2014-06-03 13:30:15 -04:00
c32917f6c1 telepathyClient: Remove all the fancy features
This can be done with another app, like Empathy or Chat.
2014-06-03 12:32:56 -04:00
182d45dace autorunManager: Remove the resident "Removable Devices" notification
Users aren't usually the best at obeying the rules, and systems can
deal with hotplug without ejecting first.

https://bugzilla.gnome.org/show_bug.cgi?id=719857
2014-06-03 12:32:56 -04:00
829e7623df css: Clean up slightly
Remove the networking stuff from the message tray section.
2014-06-03 12:32:56 -04:00
89675c9061 Updated Brazilian Portuguese translation 2014-06-03 13:29:01 +00:00
f9df83802d ui: Adapt to display.get_tab_list API change 2014-06-03 14:52:18 +02:00
e51eb723fc windowMenu: Do a better job with faking the source actor
The 0x0 dummyCursor works well when the menu pops up directly underneath
the pointer (e.g. when triggered by right-clicking the titlebar) or by
keyboard, but not when triggered by the menu button - the menu does not
point to the center of the button's bottom edge, and unless the user
keeps holding the mouse button while moving into the menu, the menu will
be dismissed immediately on button release.
Address these issues by using the button geometry to overlay the window
button with an appropriately sized actor that acts as a proper sourceActor,
to make the window menu behavior consistent with other shell menus.

https://bugzilla.gnome.org/show_bug.cgi?id=731058
2014-06-02 23:48:53 +02:00
5b61f2d642 windowMenu: Implement new show_menu_for_rect() hook
Having the full geometry of the menu's source button (if any) will
allow us to address several misbehaviors of window menus, so use
that instead of show_menu().

https://bugzilla.gnome.org/show_bug.cgi?id=731058
2014-06-02 23:48:53 +02:00
094669baee messageTray: Clear the pointer left timeout when showing a notification
When the pointer leaves the notification area, we queue a timeout to
hide the notification after a little while. If the user is hovering over
a notification and clicks the X button to close the notification, we will
destroy the notification, which causes a "pointer left" event on the
notification area. This queues a timeout which erroneously fires after
the next notification in the queue shows up.

The code and state machine are too complex to properly make sure this
timeout doesn't fire when there is no notification up next, so instead
just clear it when showing a notification to make sure that any
previously queued timeout doesn't apply to us.

https://bugzilla.gnome.org/show_bug.cgi?id=731118
2014-06-02 17:39:52 -04:00
f6b5385495 messageTray: Squash together some duplicated code
https://bugzilla.gnome.org/show_bug.cgi?id=731118
2014-06-02 17:39:51 -04:00
524e2df708 messageTray: Don't exit out early if we have a left timeout
Otherwise, we won't mark the pointer as hovering on the notification.

https://bugzilla.gnome.org/show_bug.cgi?id=731118
2014-06-02 17:39:51 -04:00
9f887d9a28 Updated Lithuanian translation 2014-06-02 23:35:53 +03:00
214a41793f build: Remove GMenu include
The actual GMenu dependency was removed a while ago, so stop adding
it to the GIR includes.
2014-06-02 17:56:38 +02:00
2e40ffc558 Updated Italian translation 2014-05-31 12:55:41 +00:00
3584887938 openbsd: fix shell_global_reexec_self()
Rework the way we re-exec the shell on OpenBSD so that it does not only
work the first time it is re-exec'd.
Plug a small leak in the __linux__ case while here.

https://bugzilla.gnome.org/show_bug.cgi?id=727763
2014-05-30 11:26:39 +02:00
0d6c002b8e calendar: Port EventsList to ClutterTableLayout
We don't make use of any functionality StTable provides over
ClutterTableLayout, so port all users to the Clutter layout
in order to remove our own copy of the code.

https://bugzilla.gnome.org/show_bug.cgi?id=703833
2014-05-28 22:11:38 +02:00
ec714864f2 calendar: Port calendar to ClutterTableLayout
We don't make use of any functionality StTable provides over
ClutterTableLayout, so port all users to the Clutter layout
in order to remove our own copy of the code.

https://bugzilla.gnome.org/show_bug.cgi?id=703833
2014-05-28 22:11:38 +02:00
1b77149ec9 build: Remove ShellNetworkAgent documentation
It is only an internal implementation detail, and it also causes build problem
when NetworkManager is disabled.

https://bugzilla.gnome.org/show_bug.cgi?id=726460
2014-05-28 22:07:19 +02:00
93c9e031e3 Update (allow-none) annotations
The annotation has been deprecated in favor of (nullable) and/or
(optional).
2014-05-28 22:01:55 +02:00
3ff4277f86 workspace: Don't allow closing windows with attached modals
Modal dialogs prevent the parent from being closed in "normal mode",
so it makes sense to not allow it in the overview either.

https://bugzilla.gnome.org/show_bug.cgi?id=729886
2014-05-28 22:01:54 +02:00
2a63267be0 loginDialog: Remove unused variable 2014-05-28 22:01:54 +02:00
2b365627ed windowManager: Remove (un)blockAnimations()
They are no longer used, kill them.
2014-05-28 22:01:53 +02:00
b8f0d0f0dc telepathyClient: Only use 12-hour format when supported by the locale
It is a bit odd to request AM/PM format when the locale selected
by LC_TIME lacks the concept. We ignore the format setting in that
case elsewhere and assume 24-hour format, let's do the same for
chat timestamps for consistency.

https://bugzilla.gnome.org/show_bug.cgi?id=728271
2014-05-28 22:01:53 +02:00
df1bed941d telepathyClient: Remove unused variable 2014-05-28 13:09:43 -04:00
604085fdb9 Update British English translation 2014-05-27 21:44:13 +01:00
2d3c81390b Bump version to 3.13.2
Update NEWS.
2014-05-27 21:35:37 +02:00
516b8f6bf8 screenshot: Don't use meta_window_get_rect
It's going to be removed soon.
2014-05-27 15:14:33 -04:00
75fdca0b47 data: Drop button-layout from overrides schema
The default in gsettings-desktop-schemas now matches what we want,
so no need to keep overriding it.
2014-05-27 19:54:31 +02:00
9f366118f0 windowMenu: Only add workspace actions when enabled by session mode
When workspaces are disabled by the session mode, all workspace
related actions don't make sense. Worse, the "Move to Workspace ..."
actions allow sending windows into a nirvana with no means to
get them back.

https://bugzilla.gnome.org/show_bug.cgi?id=730653
2014-05-27 19:49:06 +02:00
c228a9a89a viewSelector: Don't re-navigate into the active page
Starting keynav into the active page is handled from a key-press
handler on the stage, however we should not "start" keynav when
we are already navigating elsewhere - the latter can happen when
keynav fails (for instance because the focus is trapped inside an
open app folder or at the end of the dash), and the event bubbles
up to the stage. So make sure to only handle the event to actually
start keynav, to not interfere with the normal navigation handling.

Thanks to Carlos Soriano <carlos.soriano89@gmail.com> for the
debugging footwork.

https://bugzilla.gnome.org/show_bug.cgi?id=726760
2014-05-27 19:49:06 +02:00
e747fcb16f windowMenu: Ping window when activating any menu action
We do this for actions in the regular app menu, it makes sense to
do the same for window menu and fallback app menu.
2014-05-27 19:49:05 +02:00
a72a24ebff theme: Bring fallback app-menu style closer to GTK+
The fallback app-menu in GTK+'s client side decorations obviously
uses the GTK+ theme rather than the shell one; update the style
of our own fallback app-menu to resemble that style.

https://bugzilla.gnome.org/show_bug.cgi?id=730752
2014-05-27 19:49:05 +02:00
8811ba2ec0 Support (fallback) app menu in SSD
We now allow "appmenu" in the button layout to make synchronizing it
with GTK+'s client-side decorations easier, but as some people tweak
their settings to get in-window app menus even when using the shell,
actually pop up the app menu when the button is activated.

https://bugzilla.gnome.org/show_bug.cgi?id=730752
2014-05-27 19:49:05 +02:00
38d8e465b3 popupMenu: Hide separator label when not in use
The optional label support introduced in commit af063dc2f2 broke
the centering of separators, as the label still adds additional
spacing even when empty.
Properly hide the label actor in that case to fix the alignment.

https://bugzilla.gnome.org/show_bug.cgi?id=730753
2014-05-27 19:49:04 +02:00
5b3fb024be extensionPrefs: Skip main window when launched with a UUID
The extension-prefs tool is used by gnome-tweak-tool and the
extensions web site to display preferences. However as those
already implement their own extension lists, the main window
is not useful in that context (to not say it is rather silly).

Just skip the main window and only show the specified extension's
preference dialog in those cases.

https://bugzilla.gnome.org/show_bug.cgi?id=730829
2014-05-27 18:02:25 +02:00
521f5f2b6b extensionPrefs: Add switches to enable/disable extensions
Bring the extension-prefs tool in line with the mockup by adding
switches to enable/disable extensions, similar to the extension
page in gnome-tweak-tool.

https://bugzilla.gnome.org/show_bug.cgi?id=730829
2014-05-27 18:02:25 +02:00
e1b30b2924 extension-prefs: Give the UI a bit of GNOME 3 treatment
The extension-prefs UI has never been great, but as the GNOME 3
design patterns are evolving, it is starting to look seriously
outdated. Modernize the UI a bit to have it fit in a bit better.

https://bugzilla.gnome.org/show_bug.cgi?id=730829
2014-05-27 18:02:25 +02:00
dd85670f8b switcherPopup: Add a return value to _keyPressHandler
This allows us to handle keybindings that use Escape instead of just
dismissing the popup unconditionally when we see an Escape press.

https://bugzilla.gnome.org/show_bug.cgi?id=730739
2014-05-26 15:55:37 +02:00
625f3a5113 tools/build: Update to 3.14 2014-05-25 23:22:18 +02:00
58c4a6c847 Updated Hebrew translation 2014-05-22 23:46:51 +03:00
752aca811c windowManager: Support coords 2014-05-22 10:51:49 -04:00
a4cf0501ee Updated Indonesian translation 2014-05-22 09:22:25 +00:00
4c7b992c36 windowMenu: Fix typo
Closing windows is done with delete(), not close() ...
2014-05-21 19:57:17 +02:00
264a51de3f gnome-shell-wayland.desktop: Drop mutter-launch from Exec line
mutter-launch has been replaced with logind's Session and Seat APIs.
2014-05-21 11:11:27 +02:00
625280bcd6 Updated Brazilian Portuguese translation 2014-05-20 19:36:14 +00:00
4f8265f82e Updated Spanish translation 2014-05-20 20:05:06 +02:00
ff07d3a46a Renable compiler warnings and fix a few that show up
The switch to GNOME_COMPILE_WARNINGS() caused -Wall and other
warnings to not actually be used since GNOME_COMPILE_WARNINGS()
just sets WARN_CFLAGS. Add WARN_CFLAGS to AM_CFLAGS so that
it takes effect.

Add  -Wno-error=deprecated-declarations so that when -Werror is
enabled, we don't fail on all the deprecated cogl and clutter
symbols.

https://bugzilla.gnome.org/show_bug.cgi?id=730408
2014-05-19 20:51:12 -04:00
5d11941638 Support resource:/// URL's in GNOME_SHELL_JS envvar
It can be useful to augment the shell's search path by doing

 GNOME_SHELL_JS=resource:///org/gnome/shell:<mypath>

But this doesn't work because resource: is split off. Special
case path elements that are just 'resource' and recombine
them with the next element.

https://bugzilla.gnome.org/show_bug.cgi?id=730409
2014-05-19 20:47:05 -04:00
f1f659571b Updated Turkish translation 2014-05-19 11:46:28 +00:00
c60d13b33e Updated Lithuanian translation 2014-05-18 22:25:40 +03:00
33060d382b Updated Czech translation 2014-05-18 15:40:01 +02:00
4b1e412ac3 Updated Hebrew translation 2014-05-18 02:48:02 +03:00
659730ab09 Mark string as translatable 2014-05-18 01:08:49 +02:00
7c3a99b7ed Updated POTFILES.in 2014-05-18 01:02:36 +02:00
e7af257814 Implement window menus in gnome-shell
https://bugzilla.gnome.org/show_bug.cgi?id=726352
2014-05-17 15:17:29 -04:00
c9190294bc gnome-shell-build-setup.sh: work around removal of gpk-install-package-name
gpk-install-package-name was removed in 3.12. Call the D-Bus interface
using gdbus instead.
2014-05-17 15:10:04 -04:00
e070e3c44a Updated Czech translation 2014-05-16 14:37:15 +02:00
fe87de7cec Updated Hebrew translation 2014-05-16 04:20:15 +03:00
2519e4f08d Updated Brazilian Portuguese translation 2014-05-15 18:28:01 +00:00
fe304d3c94 Updated Lithuanian translation 2014-05-11 23:43:13 +03:00
1f4e6872ab Updated Spanish translation 2014-05-10 19:48:56 +02:00
5ade2e7418 main: emit systemd message for any session except gdm and initial-setup 2014-05-09 23:35:57 +02:00
3e7d325e77 [l10n] Updated Catalan (Valencian) translation 2014-05-09 00:02:51 +02:00
ef04a9d1ed osdWindow: Check monitor validity before updating
When a monitor is removed, the OsdWindow for that monitor may process
the monitors-changed signal before OsdWindowManager does (which will
remove the OSD). If that happens, we will currently try to access
an invalid monitor; check for this to avoid a couple of warning.
2014-05-08 11:14:23 +02:00
011fef4b2b system: Keep key focus when switching between alternatives
When switching between alternatives in AltSwitcher, the currently
visible child is replaced with the alternative. If the original
child has the key focus when it is removed from the stage, the
focus is lost. Detect this case and manually move the focus to
the new child.

https://bugzilla.gnome.org/show_bug.cgi?id=727259
2014-05-06 14:25:39 +02:00
caf6389f79 Fix Italian translation
- "Aereo" is the thing that flies, "Aero" is the Windows 7 theme.
- "Abilita", not "Abilitato", for the Enable action.
2014-05-04 15:22:55 +02:00
c7306449ae workspaceThumbnails: Move to primary monitor first when creating workspace
When dragging a window preview from a non-primary monitor onto a
workspace thumbnail, the window is moved to the primery monitor
first before changing its workspace. However when dragging the window
between thumbnails to create a new workspace, it is kept on its current
monitor instead. This is not only inconsistent, but outright confusing
with the default 'workspaces-only-on-primary' setting, as the newly
created workspace is immediately removed again.

https://bugzilla.gnome.org/show_bug.cgi?id=683819
2014-05-03 23:50:25 +02:00
8c45e6fa43 main: Don't depend on GSystem unconditionally
We only need GSystem when running under systemd. As libgsystem itself
has a hard dependency on systemd, only import it when actually needed
to keep working on systems where systemd is not available.

https://bugzilla.gnome.org/show_bug.cgi?id=728449
2014-05-03 23:50:25 +02:00
9504d21297 Updated Belarusian translation.
(cherry picked from commit 6d679148b6)
2014-05-03 14:46:57 +02:00
0832ca544a Update the default favourite apps list for renamed desktop files
Nautilus and gnome-documents have changed their desktop file names to
support DBus activation.

https://bugzilla.gnome.org/show_bug.cgi?id=729429
2014-05-03 00:01:56 +02:00
a000a1f76e rfkill: make the Airplane mode menu insensitive in the lock screen
Like we do for the bluetooth and wifi menus

https://bugzilla.gnome.org/show_bug.cgi?id=729224
2014-04-30 21:52:56 +02:00
ea8b02ff7f Bump version to 3.13.1
Update NEWS.
2014-04-30 19:15:12 +02:00
3cc7112283 Add a 'Show Details' menuitem in the app picker
Add a menu item that shows the details of the application
in gnome-software.

https://bugzilla.gnome.org/show_bug.cgi?id=643043
2014-04-30 19:15:06 +02:00
50f0fc4e23 overviewControls: Adjust heuristic for hiding workspace switcher
The workspace switcher should be expanded when workspaces are in use.
Our current implementation assumes that workspaces are used when there
are windows on at least two workspaces. However workspaces are already
used when moving from a non-empty workspace to an empty one (presumably
with the intention to launch something on that workspace), so tweak the
heuristic accordingly.

https://bugzilla.gnome.org/show_bug.cgi?id=662457
2014-04-30 18:06:52 +02:00
0a4ad01d8a Updated Galician translations 2014-04-30 15:34:42 +02:00
19afabe2a1 dateMenu: Don't make dateMenuButton reactive on current day
Having the ability to go to the current date if the user is already
on the current date can be confusing. So don't make the button reactive
until the selected date changes.

https://bugzilla.gnome.org/show_bug.cgi?id=726724
2014-04-29 01:02:27 +02:00
103027a446 St: remove hover state if reactive disabled
The hover state of a widget can become persistent if
the widget becomes reactive while a pointer grab.
To avoid that, remove hover state if the reactive property
is disabled.

https://bugzilla.gnome.org/show_bug.cgi?id=728343
2014-04-29 01:02:27 +02:00
28c1f81f4a appDisplay: Make page panning smoother on touch
Currently, our logic for page panning isn't great. If the user starts a
pan upwards and hesitates over a new page, we'll go to the *next* page
on release, since the difference is greater, but the velocity wound down
to 0.

Instead of trying to treat it like page down or scrolls, simply do the
math to find the page where the user scrolled to.

This is unfortunately broken for fast swipes, since the user doesn't get
far enough into the new page to make a difference. I'm getting the
impression we'll need a gesture recognizer for this, though, however
crude. Simple hacks I tried, like a velocity multiplier, didn't work
properly.

https://bugzilla.gnome.org/show_bug.cgi?id=729064
2014-04-28 13:30:09 -04:00
82ec6c08b8 appDisplay: Fix pages getting "stuck" under touch
We often call goToPage like:

  this.goToPage(this._currentPage - 1);

(or + 1). During panning, these are based on the velocity values
of the gesture action. If we're already on the first or last page
and the panning ends, it's possible to get goToPage that's either
-1 or greater than the last page.

During normal usage, this isn't a problem, since the Y values will
be correct, always. But when panning, the Y values stick to the
finger, and thus if we return early, we won't snap to the exact page,
making it seem like things get "stuck".

Fix this the simple way by clamping to the correctly-ranged values
of pageNumber

https://bugzilla.gnome.org/show_bug.cgi?id=729064
2014-04-28 13:30:09 -04:00
551e57406d appDisplay: Remove redundant checks for pageNumber
We already do these at the beginning of the function.

https://bugzilla.gnome.org/show_bug.cgi?id=729064
2014-04-28 13:30:08 -04:00
1c8036b863 If systemd is enabled send a message with MESSAGE_ID after gnome-shell has started in user mode
Also adds a new dependency - libgsystem
https://bugzilla.gnome.org/show_bug.cgi?id=728449
2014-04-28 18:49:44 +02:00
f8bac5c197 util: Don't pass too many arguments to child_watch_add 2014-04-28 10:22:51 -04:00
6ece67b654 calendar: Don't mark translations from GTK+ for translation
The order in which month and year are displayed is controlled by
a "special" translated string in GTK+. We pick up the translation
from there, so make sure that it doesn't get translated again
in gnome-shell.

https://bugzilla.gnome.org/show_bug.cgi?id=715042
2014-04-27 02:06:27 +02:00
96411dfed5 Updated Brazilian Portuguese translation 2014-04-26 22:14:01 +00:00
24897169a9 st-entry: Handle <shift>insert
GTK+ supports this as alternative to <ctrl>v, so let's do the same
for consistency.

https://bugzilla.gnome.org/show_bug.cgi?id=648318
2014-04-26 21:19:59 +02:00
05ddece9a0 workspaceThumbnails: Fix removal of multiple workspaces
When workspaces have been removed, we need to remove the corresponding
thumbnails as well; the number of thumbnails that need removing is
the difference between the old number of workspaces and the new one.
Currently we assume that the old number of workspaces corresponds to
the number of existing thumbnails, but that may actually be wrong:
A thumbnail will still be animated out after its workspace has been
removed. As a result, we end up removing too many thumbnails when a
workspace is removed while a thumbnail of a previously removed workspace
is still animating out. Fix this by basing the old number of workspaces
only on thumbnails that have not been removed previously.

https://bugzilla.gnome.org/show_bug.cgi?id=728820
2014-04-26 17:50:38 +02:00
df08ae7996 screenshot: Port to GrabHelper
Taking an area screenshot doesn't work currently when in "grab mode",
for instance when the message tray or top bar menus are open. Fix
this by using GrabHelper for selecting the area, so grabs are properly
stacked for us.

https://bugzilla.gnome.org/show_bug.cgi?id=709126
2014-04-26 16:19:21 +02:00
a244c1e987 loginManager: Kill ConsoleKit support
The code path is completely unmaintained and untested (and probably
unused as well, considering that nobody has complained about accessing
the session object's Active property which does not exist in the
ConsoleKit case).
Most of our ConsoleKit code is already a dummy anyway, just do the
same for the remaining functionality.

https://bugzilla.gnome.org/show_bug.cgi?id=686626
2014-04-25 15:53:45 +02:00
bc182f78b1 style: Clean out unused selectors 2014-04-25 15:51:43 +02:00
2d18b06b3f osdWindow: Fix setting a zero-level in osdWindow
Commit 7101cc3170 caused a small
regression insomuch that it checks for a valid "level" but simply using
"if (level)" which will be false if level is undefined and if
level == 0.

Check for not undefined instead.

https://bugzilla.gnome.org/show_bug.cgi?id=727384
2014-04-25 15:20:18 +02:00
61852df9a1 Tajik translation updated 2014-04-25 17:16:49 +05:00
3969be38bd environment: Be more careful when hooking up style properties
Sometimes it is more appropriate to set layout properties that are
hooked up to CSS properties in code. However this is currently not
possible, as we end up setting properties to 0 when not found in CSS;
be a bit more careful when hooking up CSS properties to support this.

https://bugzilla.gnome.org/show_bug.cgi?id=728897
2014-04-24 19:31:55 +02:00
92f9aff784 tests/gapplication: Don't pass too many args to gtk_init 2014-04-23 16:49:44 -04:00
815cfe6c20 Updated Romanian Translation 2014-04-23 21:46:14 +02:00
052b5176d2 Updated Czech translation 2014-04-23 17:05:29 +02:00
bccbcd8e4a networkAgent: Handle empty vpn 'keyfiles'
Passing back no data is valid in case both secrets (group and user passwords)
are stored.

Just confirm the request in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=728681
2014-04-23 13:09:34 +02:00
90589fabee texture-cache: Fix compile warning 2014-04-22 18:35:56 -04:00
5947111f14 Updated Ukrainian translation 2014-04-22 23:07:18 +03:00
c8c56a5443 Updated Khmer translation 2014-04-22 08:46:49 +00:00
a83b9ed6d7 search: Make sure to destroy old provider displays when we unregister
When we unregistered providers, like when we refreshed the list of
active remote providers, we would forget to destroy the old provider
display after the fact. This left an empty "skeleton" provider display
still in the search results that would never be filled in. Make sure
to destroy it properly.

https://bugzilla.gnome.org/show_bug.cgi?id=728597
2014-04-21 23:36:52 -04:00
f9c83bccb6 status: use consistent messages for network connections
https://bugzilla.gnome.org/show_bug.cgi?id=727163
2014-04-21 22:03:22 +02:00
2f720e22fc rfkill: fix turning off airplane mode from the menu
The menu does not have a proxy anymore, it needs to go through
the manager.

https://bugzilla.gnome.org/show_bug.cgi?id=728512
2014-04-21 19:10:54 +02:00
329028d3b9 Updated Dutch translation 2014-04-18 20:16:51 +02:00
5c3f9f6999 osdWindow: Show on all monitors
We had one osdWindow that we displayed either on the primary monitor or on
whatever monitor index got passed over dbus.

Change that to show the osd on all monitors when no explicit monitor is
requested. A monitor should be requested in cases like display brightness where it makes sense to only show the osd on the affected monitor.

https://bugzilla.gnome.org/show_bug.cgi?id=722684
2014-04-17 15:15:14 +02:00
4a6b89d44c windowManager: add switch-to-workspace-last keybinding
When using dynamic workspace, the number of the last workspace may vary,
and it would be nice to have a keybinding to jump directly to it.

https://bugzilla.gnome.org/show_bug.cgi?id=659288
2014-04-16 21:45:14 +02:00
7fa1834ab6 extension-prefs: Remove extra whitespace 2014-04-15 17:54:51 -04:00
c7bc9f8925 extension-prefs: Kill warning
We need to include shell-js.h
2014-04-15 17:54:51 -04:00
70d75ca311 loginManager: Don't pass too many args to steal_fds 2014-04-15 17:53:37 -04:00
9830b3b8f7 appDisplay: Don't pass too many args to add_actor 2014-04-15 17:53:34 -04:00
525c8780fd Fix typo 2014-04-15 23:35:06 +02:00
76c4ec8ee4 layout: Don't always extend struts to the screen edge
NetWM struts are defined in terms of screen edges (rather than monitor
edges), which works poorly with vertical monitor layouts (as it renders
entire monitors unusable). Don't extend struts in those cases.

https://bugzilla.gnome.org/show_bug.cgi?id=663690
2014-04-15 23:35:06 +02:00
b4a48a7644 extension-prefs: Force linking with libgnome-shell-js
Use the same hack we use for the main executable to fool the linker
when using --as-needed.

https://bugzilla.gnome.org/show_bug.cgi?id=727948
2014-04-15 23:35:05 +02:00
5087f0930c l10n: Update Japanese translation 2014-04-15 00:36:54 +09:00
398cc5af85 Updated Danish translation 2014-04-13 21:53:58 +02:00
f073945d31 Updated Hungarian translation 2014-04-13 15:42:18 +02:00
c36abcb905 Updated Latvian translation 2014-04-12 12:35:33 +03:00
e2ccbe5528 layout: Change setDummyCursorPosition to also set the size
If we are being used to follow a text entry cursor we also need to set
the size so that the popup menu avoids covering it.

https://bugzilla.gnome.org/show_bug.cgi?id=727579
2014-04-11 16:54:01 +02:00
68b2d5fcf0 LookingGlass: Correct timeout usage
Calling Mainloop.source_remove() on the timeout ID will not reset it,
and we're already removing the timeout by returning GLib.SOURCE_REMOVE
in the callback.
2014-04-10 21:08:49 +02:00
cd2bd7685a js: Name all the timeouts and idles
With very uninventive names. Names now, good names later.

https://bugzilla.gnome.org/show_bug.cgi?id=727983
2014-04-10 21:08:16 +02:00
85f811f147 src: Name all the timeouts and idles
https://bugzilla.gnome.org/show_bug.cgi?id=727983
2014-04-10 19:38:04 +02:00
e8fd8b58d0 Added Khmer translation 2014-04-10 07:30:29 +00:00
2aa12e8f4b Updated Indonesian translation 2014-04-10 05:48:25 +00:00
ee0c76c2b9 Link to libmutter always
Wayland support was merged into mutter master.
2014-04-09 15:24:09 -07:00
6ce6e77d2a Tajik translation updated 2014-04-09 11:38:37 +05:00
0117fcb0e7 location: Don't hide location menu
If geoclue reports that we can't aquire location, we hide the menu. This
will typically happen when user is offline (and doesn't have a 3G
modem). This is likely not what we want since this like a temporary
situation and user would want the ability to toggle geolocation still
even if its currently not possible any applications to query the
location.

https://bugzilla.gnome.org/show_bug.cgi?id=727398
2014-04-08 14:11:28 +01:00
79bebe849d Updated Portuguese translation 2014-04-07 21:34:08 +00:00
25eadc5559 Updated Italian translation 2014-04-06 12:17:24 +00:00
a5784484e0 doap: update URLs 2014-04-04 19:32:09 +02:00
a701b006c5 Updated Italian translation 2014-04-03 07:08:23 +00:00
2d68bbf94e theme: Fix bad antialising on panel menu buttons
https://bugzilla.gnome.org/show_bug.cgi?id=727336
2014-04-01 19:47:28 +02:00
df305314c1 a11y: setting accessible role and label for windows at the overview
https://bugzilla.gnome.org/show_bug.cgi?id=726670
2014-04-01 13:56:53 +02:00
d03239c009 [l10n] Update Catalan translation 2014-03-31 23:53:35 +02:00
71ccad4399 Updated Galician translations 2014-03-31 23:35:41 +02:00
c916d43688 Updated Slovenian translation 2014-03-31 20:51:33 +02:00
3e6d0bc252 Updated French translation 2014-03-31 16:26:37 +00:00
79d0a848a4 Updated Russian translation 2014-03-31 15:48:54 +00:00
a6af33d450 Updated Lithuanian translation 2014-03-30 23:02:54 +03:00
c6664adcce Updated Swedish translation 2014-03-30 19:05:44 +00:00
4e8a9470d1 Updated Basque language 2014-03-30 12:56:28 +02:00
1b88df9439 update zh_CN translation 2014-03-30 16:57:39 +08:00
ec42278654 Updated German translation 2014-03-29 08:36:27 +01:00
f435f249d0 Updated Korean translation 2014-03-29 16:13:54 +09:00
9c88fec4fc texture-cache: use scale factor for load_uri_async()
https://bugzilla.gnome.org/show_bug.cgi?id=726907
2014-03-28 10:53:01 -07:00
9ecf466ce1 texture-cache: use scale factor to load background and borders
https://bugzilla.gnome.org/show_bug.cgi?id=726907
2014-03-28 10:53:01 -07:00
a22fdea0e3 border-image: add support for scale factor
In a later commit we'll add support for rendering borders and
backgrounds with scale factor.

https://bugzilla.gnome.org/show_bug.cgi?id=726907
2014-03-28 10:53:01 -07:00
90e52d7266 texture-cache: always call gdk_pixbuf_loader_set_size
Just use the regular image size if we don't want to scale it. This is a
refactor in preparation for the next commit.

https://bugzilla.gnome.org/show_bug.cgi?id=726907
2014-03-28 10:53:01 -07:00
74d9b6c2bf texture-cache: use scale factor to load sliced image
We need to use a GdkPixbufLoader instead of the straightforward
gdk_pixbuf_new_from_file(), since we want to load the image already
scaled if possible - e.g. if it's an SVG file.

https://bugzilla.gnome.org/show_bug.cgi?id=726907
2014-03-28 10:53:01 -07:00
b1e9873de5 Updated Spanish translation 2014-03-28 13:44:52 +01:00
6fdc52a64a Updated Indonesian translation 2014-03-28 11:58:53 +00:00
b7d6792de9 Updated Greek translation 2014-03-28 09:34:14 +02:00
c78dc55e65 Updated Czech translation 2014-03-27 22:17:34 +01:00
af74bded14 Updated Brazilian Portuguese translation 2014-03-27 20:53:59 +00:00
e3c9a9c3e4 Updated Hebrew translation 2014-03-27 20:25:06 +02:00
c68eecaf1c Updated Polish translation 2014-03-27 19:23:43 +01:00
b0bdf7f6c3 location: Improved language
* 'Turn On' -> 'Enable'
* 'Turn Off' -> 'Disable'
* 'Off' -> 'Disabled'
* 'On' ->  'In Use' or 'Enabled' depending on whether or not service is
  in use.

https://bugzilla.gnome.org/show_bug.cgi?id=726498
2014-03-27 17:42:11 +00:00
d0f69a72dc appDisplay: Ensure the currently focused icon is viewable
We either scroll or paginate to the correct place when an icon gets
key focus.

https://bugzilla.gnome.org/show_bug.cgi?id=726759
2014-03-26 11:42:34 +01:00
8d8c75d32d iconGrid: Add a key-focus-in signal
This fires whenever a grid's child emits its own key-focus-in signal.

https://bugzilla.gnome.org/show_bug.cgi?id=726759
2014-03-26 11:42:33 +01:00
e339e2658d search: Ensure that the default result is visible in the scroll view
The default result is set to selected when key focus enters the search
entry. We must also scroll the view if needed.

https://bugzilla.gnome.org/show_bug.cgi?id=726759
2014-03-26 11:42:32 +01:00
bc4a75a732 Update Arabic translation 2014-03-26 01:41:24 +02:00
e09e1bc3f5 Specifically ask for Telepathy 0.x
Telepathy 1.0 will not be compatible, and will probably require
source changes. telepathy-glib 0.12 and telepathy-logger 0.2 are
the 0.x ABIs (they were the first stable-branches to have g-i).

Bug: https://bugzilla.gnome.org/show_bug.cgi?id=721704
Reviewed-by: Giovanni Campagna
2014-03-25 17:19:28 +00:00
87c50eb495 Bump version to 3.12.0
Update NEWS.
2014-03-25 16:24:42 +01:00
be291ee4f9 loginScreen: reset greeter when coming back to login screen
When a user logs in to a wayland session, we keep the login screen
running on the X server with the login screen running in a deactivated mode.

This commit makes sure it get reactivated when the user comes back to
the VT (from user switching, logout or just ctrl-alt-f1).

https://bugzilla.gnome.org/show_bug.cgi?id=726989
2014-03-25 10:41:12 -04:00
e634b49859 Updated slovak translation 2014-03-24 23:40:30 +01:00
4dddaefa41 Updated Japanese translation 2014-03-23 23:22:30 +00:00
cda60455f0 Update Danish translation 2014-03-23 23:52:56 +01:00
a42b8870b0 Update Danish translation 2014-03-23 23:36:42 +01:00
daa66a6de6 Updated Danish translation 2014-03-23 23:29:51 +01:00
6c6aed84bc Updated Danish translation 2014-03-23 23:09:56 +01:00
c59314acc1 Updated Serbian translation 2014-03-23 11:12:07 +01:00
dd3cc78be5 French translation change for "Location" 2014-03-22 15:35:49 +01:00
54b0b6eec5 Updated Traditional Chinese translation(Hong Kong and Taiwan) 2014-03-22 20:04:22 +08:00
41654b22b3 Updated Slovenian translation 2014-03-22 10:03:14 +01:00
07cc60d65a Updated Slovenian translation 2014-03-21 21:44:00 +01:00
a7f9dc5114 Updated Telugu translation 2014-03-21 22:04:33 +05:30
b5ae23d544 Updated Arabic translation 2014-03-21 11:07:50 +01:00
75347cb4f7 Updated Arabic translation 2014-03-21 10:53:17 +01:00
03a44b6ec2 Updated Basque language 2014-03-20 22:56:30 +01:00
17d2349c49 Updated Russian translation 2014-03-20 23:00:58 +04:00
4e98c44052 Update Aragonese translation 2014-03-20 19:39:33 +01:00
0bef281d66 Bump version to 3.11.92
Update NEWS.
2014-03-19 22:07:59 +01:00
54af25ec24 Updated Danish translation 2014-03-19 22:04:16 +01:00
86ab02f400 Updated Hungarian translation 2014-03-19 09:47:15 +00:00
ae01cd143f Updated Norwegian bokmål translation. 2014-03-18 22:52:17 +01:00
2974b29f15 layout: Create a group for modal dialogs
This way we can ensure that they're always stacked under the OSK.

https://bugzilla.gnome.org/show_bug.cgi?id=719451
2014-03-18 17:03:39 +01:00
1b78dd662b modalDialog: Remove unused parentActor param
No ModalDialog users set this so let's hardcode it.

https://bugzilla.gnome.org/show_bug.cgi?id=719451
2014-03-18 17:03:38 +01:00
c22264a0ca BluetoothMenu: fix visibility
In addition to BluetoothAirplaneMode, we need to check also
BluetoothHasAirplaneMode, which is indicative of bluetooth rfkill
devices (and by extension bluetooth adapters).
This prevents showing the menu if there is no adapter present.

https://bugzilla.gnome.org/show_bug.cgi?id=725057
2014-03-17 16:45:30 +01:00
48c3e3f534 Tajik translation updated 2014-03-17 12:26:07 +05:00
6ccc134ba6 Updated Greek translation 2014-03-16 22:22:29 +02:00
9aa36d7851 Finnish translation update by Jiri Grönroos 2014-03-16 20:35:48 +02:00
3278f77739 Updated Hebrew translation. 2014-03-16 16:15:10 +02:00
5d24f48e3b Updated Ukrainian translation 2014-03-16 15:57:12 +02:00
573c1c86cc focus-manager: Make C ANSI-compliant 2014-03-15 15:44:40 -04:00
374c5967ba Updated French translation 2014-03-15 15:58:04 +00:00
66b71a36ce app-system: Filter out stale apps from cache
Since rebasing our AppSystem on GLib's facilities, we only ever
append to the id-to-app cache. So if an application is uninstalled,
shell_app_system_lookup_app() will still happily return it if it
was cached previously. For instance if a favorite app is uninstalled,
it keeps lurking in the dash until a restart.
To fix, filter out removed apps from the cache when handling
GAppInfoMonitor::installed-changed.

https://bugzilla.gnome.org/show_bug.cgi?id=726414
2014-03-15 15:56:30 +01:00
e70fd5a57a location: Make insensitive when locked
Most system menu entries are disabled on the lock/login screen,
there is no good reason for the location entry to be an exception.

https://bugzilla.gnome.org/show_bug.cgi?id=726319
2014-03-15 14:19:02 +01:00
4589ce4d78 bluetooth: Make insensitive when locked
Most system menu entries are disabled on the lock/login screen;
there is no good reason why users should be allowed to turn bluetooth
on/off (but not e.g. Wifi), so disable the entry as well.

https://bugzilla.gnome.org/show_bug.cgi?id=726319
2014-03-15 14:19:01 +01:00
d6197b0904 background: Destroy redundant backgrounds right after loading
When reacting to background settings changes, we may end up queuing
more than one load. The redundant backgrounds are expected to be
destroyed when the previous background has faded out; however since
commit 933f38390b, the tweened actor is the same for all
consecutive load operations and we end up with a single onComplete
handler, ergo a single destroyed actor.
As new backgrounds are always added to the bottom, we are not only
piling up additional background actors, but break changing backgrounds
more than once, as the correct background ends up being covered by
previously added redundant actors.
Fix this by destroying redundant actors right after loading rather
than waiting for the fade animation to complete.

https://bugzilla.gnome.org/show_bug.cgi?id=726120
2014-03-15 00:43:15 +01:00
30fb2b0d99 appDisplay: Improve icons of folders with few apps
Folders use a 2x2 grid of the first 4 app icons as icon - if a folder
contains less apps, we currently skip the corresponding grid positions.
As a result, the overall size request may be smaller than the one for
other icons, making the folder icon look out of place.
Fix this by always using a full grid as folder icon, using dummy actors
as necessary.

https://bugzilla.gnome.org/show_bug.cgi?id=726322
2014-03-15 00:23:50 +01:00
5cdefc324d ScreenShield: wake up the screen when resuming from suspend
At some point ScreenShield had code to do this, I don't know when
it was lost, but it makes sense and avoids having to move the mouse
just to see the shield.

https://bugzilla.gnome.org/show_bug.cgi?id=726378
2014-03-14 23:27:20 +01:00
b222d0fe44 appDisplay: Add some spacing in folder icons
With the switch to a table layout in commit f959cafb36, setting
alignments to place the individual icons at the outer edge of the grid
stopped working. Remove that code and add some explicit spacing instead.

https://bugzilla.gnome.org/show_bug.cgi?id=726323
2014-03-14 17:21:57 +01:00
fe4fddf0d5 gnome-shell-wayland.desktop: Add --display-server
This is now required by mutter to run as a native wayland compositor.
2014-03-14 16:53:59 +01:00
c675c93733 messageTray: Set the state to SHOWN when the notification is updated
If the notification is updated while SHOWING, we'll overwrite the
tween updating it to the new 'y' position, but forget to update the
state to SHOWN at the end of our transition. Make sure to always set
the state to SHOWN at the end.

https://bugzilla.gnome.org/show_bug.cgi?id=704844
2014-03-14 11:13:17 -04:00
29485ff24b layout: really queue region update before redraw
This code may have worked when written in 2009, but later gjs commit
b5e467d89aea43a8e32a1138d232c8a32e6b0785 removed the priority
parameter from idle_add.

Now, when running a constantly-updating client (es2gears) on an
embedded platform, _updateRegions() does not get called and I see
unresponsive window decorations.

Update the code to use meta_later_add() like other parts of the
shell, which is actually slightly better in this case anyway.
Solves the unresponsiveness problem.

https://bugzilla.gnome.org/show_bug.cgi?id=585500
2014-03-14 06:16:42 -06:00
f6ed3d9f88 authPrompt: Don't ever ask for a username if smartcard service is in foreground
The smartcard service is put in the foreground in two cases:

1) If password service is disabled by admin configuratoin
2) if a smartcard is inserted

In either case we don't want to ask the user to pick a user from the
userlist.  We currently only avoid asking in case 2.

This commit fixes case 1.

https://bugzilla.gnome.org/show_bug.cgi?id=726263
2014-03-13 15:08:00 -04:00
39a36cb510 Updated German translation 2014-03-13 20:01:15 +01:00
ff5550c82b util: fix "login card" smartcard detection on unlock screen
We only want to react to the card the user logged in with, at
the unlock screen.  We check "at the unlock screen" by checking
the "reauthenticating" state variable.  That variable is the
wrong one, though. It gets set too late, and in some cases, gets
set at the login screen, too.  We should be checking this._reauthOnly
instead.

This commit fixes that.

https://bugzilla.gnome.org/show_bug.cgi?id=726262
2014-03-13 14:01:02 -04:00
7d5ce1a159 screenShield: fix lifting when inserting smartcard
If a user inserts the smartcard they logged in with into the system,
it's supposed to lift the shield and prompt for pin.  That doesn't
happen because the parameter list of the smartcard-inserted signal
handler is wrong.

This commit fixes that.

https://bugzilla.gnome.org/show_bug.cgi?id=726262
2014-03-13 14:01:01 -04:00
c492415386 hidpi: Make sure gdk and clutter scaling stays disabled when the scale factor changes
Not doing that could led to messy situations when the user changes the scale factor
at runtime.

https://bugzilla.gnome.org/show_bug.cgi?id=726238
2014-03-13 14:16:11 +01:00
5616bbd45b hidpi: Listen for gtk-xft-dpi instead of monitors-changed
Currently we update the scale factor on startup and when we get a
monitors-changed signal, which is not the only cases where the setting changes. We cannot listen for gdk-window-scaling-factor changes because it is not
exported to gdk.

So use gtk-xft-dpi which also indicates a scale factor change.

When someone changes gtk-xft-dpi directly without changing the scale factor
we will just re-read the gdk-window-scaling-factor so no harm is done.

https://bugzilla.gnome.org/show_bug.cgi?id=726238
2014-03-13 14:16:11 +01:00
e117aa5297 Calendar: force-rebuild the calendar when the events change
Don't overoptimize and skip the rebuild when the month is the same,
as the calendar depends on the events too.

https://bugzilla.gnome.org/show_bug.cgi?id=726119
2014-03-13 14:12:38 +01:00
17ac1382df Updated Portuguese translation 2014-03-12 22:47:49 +00:00
057a026ea4 MessageTray: don't destroy notifications when the user presses ESC
Doing so is inconsistent with the behavior in the summary, and
is quite annoying when dealing with chats (because there is no way
to unfocus a chat notification with the keyboard only)

https://bugzilla.gnome.org/show_bug.cgi?id=724178
2014-03-12 19:33:35 +01:00
6ce6e86318 TelepathyClient: destroy an existing account notification when the user goes offline
We already filter new notifications caused by the explicit
user action, but we don't do so for changes to existing
notifications.

https://bugzilla.gnome.org/show_bug.cgi?id=723976
2014-03-12 19:26:42 +01:00
492558a2d2 search: survive the provider reporting the wrong number of metas
If a provider crashes during GetResultMetas, or returns the wrong
number of metas, report it as failing but don't cause a JS exception.

https://bugzilla.gnome.org/show_bug.cgi?id=725020
2014-03-12 19:26:41 +01:00
b78e00f372 search: destroy result actors before forgetting about them
We can't let live (ie, never destroyed) actors undergo GC, because
they will emit :destroy signals during finalization and assert/crash
libmozjs. Properly destroy all actors before letting the GC
free them.

https://bugzilla.gnome.org/show_bug.cgi?id=724798
2014-03-12 19:26:41 +01:00
c2cc504837 NetworkMenu: destroy the wifi dialog when the associate device disappears
When the device is removed by NetworkManager, we can't use it
to connect, so the dialog is useless and should be destroyed.

https://bugzilla.gnome.org/show_bug.cgi?id=723935
2014-03-12 19:26:41 +01:00
ac76940530 StTextureCache: don't crash if we fail to allocate a framebuffer
It's possible that FBO creation fails due to hw limits or the
driver not exposing the EXT_framebuffer_object extension.
In that case, just give up on creating square icons.

https://bugzilla.gnome.org/show_bug.cgi?id=724977
2014-03-12 19:26:41 +01:00
55d1c7e2ab background: fix cancellable issue
If we have the following sequence:

    cache.getImageContent({ filename: "foo", cancellable: cancellable1 });
    cache.getImageContent({ filename: "foo", cancellable: cancellable2 });
    cancellable1.cancel();

Then the second load will complete with "null" as its content, even though
it was never cancelled, and we'll see a blank image. Meanwhile, since the
second load simply appends to the list of callers for the second load,
cancellable2 does absolutely nothing: cancelling it won't stop the load,
and it will still receive onFinished handling.

To prevent this from happening, give the actual load operation its own
Gio.Cancellable, which is "ref-counted" -- only cancel it when all the other
possible callers cancel.

Based on work from Jasper St. Pierre <jstpierre@macheye.net>

https://bugzilla.gnome.org/show_bug.cgi?id=722149
2014-03-12 12:15:43 -04:00
fdf264ff64 background: get rid of nested loop when finishing file loading
At the moment when a file is loaded, we iterate through the list of
pending file loads and ignore any unrelated to the file, then iterate
all the callers of the related file loads and finish them.

In fact, there can only ever be one pending file load related to the
file, and we already know it, so we can avoid the ugly nested loops.

https://bugzilla.gnome.org/show_bug.cgi?id=722149
2014-03-12 12:15:42 -04:00
e917b7ce0f background: refactor file loading
This commit moves the code around a bit such that the
caller gets allocated up front and then a file load is either
found or created to attach the caller to.

Functionally, the code is the same, it's just now factored in a way
that will make it easier to fix a bug with cancellation later.

https://bugzilla.gnome.org/show_bug.cgi?id=722149
2014-03-12 12:12:47 -04:00
ec6facb9e7 background: always copy background content when loading into cache
Copying is actually a lightweight operation, so trying to avoid it just adds
code complexity for little gain.

Based on work from Jasper St. Pierre <jstpierre@macheye.net>

https://bugzilla.gnome.org/show_bug.cgi?id=722149
2014-03-12 12:12:47 -04:00
60f3c09f90 Updated Czech translation 2014-03-12 15:53:48 +01:00
afdfd6cebc Updated Russian translation 2014-03-11 22:30:13 +04:00
edd66c40d9 ScreenShield: send a signal to GSD to wake up the screen
Instead of poking through IDLETIME, which confuses the state tracking
and can prevent automatic suspend, send a special signal to GSD
when the screen is to be waken up for a notification.

Someday we'll bring over all the state tracking and avoid this
ping-pong between gnome-shell and gnome-settings-daemon, but
that day's not today.

https://bugzilla.gnome.org/show_bug.cgi?id=712706
2014-03-11 18:10:05 +01:00
fc4bc5277a Lightbox: complete radial effect for modal dialogs
Rework the radial effect to use similar code to MetaBackground,
and adjust parameters to make it more noticeable.

https://bugzilla.gnome.org/show_bug.cgi?id=725830
2014-03-11 17:14:07 +01:00
821768a414 network: Don't show network-offline-symbolic in the top bar
This is against the designs.

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

https://bugzilla.gnome.org/show_bug.cgi?id=721459
2014-03-11 10:01:07 -04:00
522f3bf171 NMConnectionSection: don't remove connections that were never added
NMApplet will call removeConnection() unconditionally on all sections,
including those that had nothing to do with the connection in the first
place.

Fixes:

Gjs-WARNING **: JS ERROR: TypeError: this._connectionItems.get(...) is undefined
NMConnectionSection<.removeConnection@resource:///org/gnome/shell/ui/status/network.js:323
wrapper@resource:///org/gnome/gjs/modules/lang.js:169
NMApplet<._connectionRemoved@resource:///org/gnome/shell/ui/status/network.js:1885
wrapper@resource:///org/gnome/gjs/modules/lang.js:169

https://bugzilla.gnome.org/show_bug.cgi?id=725958
2014-03-10 17:49:43 +01:00
fb7400ab85 Updated Chinese (Taiwan) translation 2014-03-09 04:26:03 +00:00
210128f22b Updated Lithuanian translation 2014-03-08 21:49:25 +02:00
78ae233823 Updated Chinese (China) translation 2014-03-08 14:04:28 +00:00
8f25da7cea Updated Latvian translation 2014-03-08 11:48:25 +02:00
7f52fdb435 update Punjabi Translation for gnome-shell: Alam 2014-03-07 19:31:38 -06:00
0ba05b29b9 loginDialog: Defer loading user list until needed
Loading the user list can be expensive, for instance when there is
a large number of users and/or their avatars have to be fetched over
the network. In case the user list is disabled anyway, there is no
point in doing that work just to hide it, so stop doing that.

https://bugzilla.gnome.org/show_bug.cgi?id=725905
2014-03-08 00:29:11 +01:00
ef8123e3a2 loginDialog: Add missing return value
_loadUserList() may be used as idle handler, so we should explicitly
return a boolean.

https://bugzilla.gnome.org/show_bug.cgi?id=725905
2014-03-08 00:29:10 +01:00
257e1f3096 calendar: Grab key focus when user changes day
When the user changes the active day by mouse click or keyboard focus
plus key press on a day in the grid, always move the keyboard focus to
the newly activated day.

This basically restores functionality that was introduced in commit
31478e9fb4 but got lost again in the re-factoring in commit
cc4659f5c6.

https://bugzilla.gnome.org/show_bug.cgi?id=725606
2014-03-07 19:18:32 +01:00
6441ae77d9 Updated Indonesian translation 2014-03-07 06:48:50 +00:00
3f0938072f Updated Kazakh translation 2014-03-07 02:35:31 +00:00
2fe06a28aa Updated Czech translation 2014-03-06 18:18:06 +01:00
38750ba798 LookingGlass: add GObject and Gio to the ns available inline
Having to go through import.gi is awfully inconvenient when one
is using the looking glass as a quick gjs console, and we're
already importing all sorts of stuff there.

https://bugzilla.gnome.org/show_bug.cgi?id=725832
2014-03-06 16:57:41 +01:00
b4c01f8905 Updated Galician translations 2014-03-06 12:27:41 +01:00
104d70c88e Updated Friulian translation 2014-03-06 11:11:35 +01:00
133a350f2f Updated Brazilian Portuguese translation 2014-03-06 08:38:30 +00:00
496ab55357 Updated Friulian translation 2014-03-06 04:59:38 +01:00
a391758e31 Updated Friulian translation 2014-03-06 04:23:40 +01:00
eaf8ad4949 Updated Friulian translation 2014-03-06 03:59:10 +01:00
2f583bdcf3 Updated Friulian translation 2014-03-06 03:45:24 +01:00
db19012a41 Bump version to 3.11.91
Update NEWS.
2014-03-06 00:23:41 +01:00
62be46884e calendar: Don't forget to rebuild the calendar
Don't forget to rebuild the calendar when changing the setting
'org.gnome.shell.calendar show-weekdate'. This wasn't happening anymore
and changing the setting resulted in a calendar without the days
grid.

https://bugzilla.gnome.org/show_bug.cgi?id=725533
2014-03-05 20:14:56 +01:00
3f2e6a48a9 Updated Hebrew translation 2014-03-05 18:58:56 +02:00
ff124e5f74 Updated Spanish translation 2014-03-05 16:58:35 +01:00
c07421c195 Calendar: make current date label clickable
Once you start navigating between months, you can't return to the
current day.  However, the current day is always displayed above the
calendar grid.  Fix this by making the current date clickable; when
clicked, the calendar grid jumps back to that day.

https://bugzilla.gnome.org/show_bug.cgi?id=641366
2014-03-05 16:40:56 +01:00
de8348d3b9 Updated Spanish translation 2014-03-05 16:39:11 +01:00
184df8a853 Updated Korean translation 2014-03-05 19:55:27 +09:00
12768a147c Updated Norwegian bokmål translation 2014-03-05 06:47:35 +01:00
94161cea37 Updated Polish translation 2014-03-04 18:18:35 +01:00
52a4ef7cf7 Updated Hebrew translation 2014-03-04 18:30:55 +02:00
3432f71500 NetworkMenu: use Connected instead of profile name for wired and wwan
The profile name is most often not useful (because it's an
autogenerated string like "Wired Connection 1"), and even when it
is, it's already available in the submenu.

https://bugzilla.gnome.org/show_bug.cgi?id=725586
2014-03-04 16:08:02 +01:00
8282aa6c24 Updated Galician translations 2014-03-03 17:34:02 +01:00
59f9eaa1c9 NetworkAgent: fix initial secrets requests after 17726abb
While the named commit was correct for VPN connections, it didn't
work correctly for the initial secrets requests like when connecting
to a new access point.  In that case, secrets *should* be requested
when none are found, but only if interaction is enabled.  The
bits of 17726abb which removed checking secrets against the hints
*were* correct, but 17726abb removed too much.

Also, to ensure passwords don't get inadvertently cleared when
simply reading them from the keyring, don't save passwords
unless something might have changed.

https://bugzilla.gnome.org/show_bug.cgi?id=724779
2014-03-03 15:17:43 +01:00
4433b735c4 Updated Lithuanian translation 2014-03-02 19:57:10 +02:00
9cd30fa6b5 Updated Polish translation 2014-03-02 17:24:18 +01:00
2c7bbfb500 Fix a typo 2014-03-02 17:21:34 +01:00
51a1d23bf9 Finnish translation update by Jiri Grönroos 2014-03-01 20:15:45 +02:00
c02e6e82bc Update Arabic translation 2014-02-28 23:09:15 +02:00
e37a3fa7e6 perf: Restore shell after runs
Currently running the perf tool results into no wm running
afterwards making it hard for the user to get the results from a terminal
and generally does not make it easy for users to run it to gather numbers.

So restore the shell after the test has completed.

https://bugzilla.gnome.org/show_bug.cgi?id=724870
2014-02-28 21:40:31 +01:00
e23c2ffecc plugin: Don't query for swap_events support directly
There is no need for the duplication, clutter already exports
this information.

https://bugzilla.gnome.org/show_bug.cgi?id=724870
2014-02-28 21:40:31 +01:00
744f11d045 Updated Spanish translation 2014-02-28 14:13:08 +01:00
b7eb1f3e8b Don't use get_core_device
clutter_device_manager_get_core_device calls XIGetClientPointer, which
requires a round-trip to the server. Since we do this on StWidget
creation, this means a full round-trip for every created StWidget.
Replace this with get_device with the ID of the VCP/VCK, since mutter
doesn't support MPX, and we know this is what the device is.
2014-02-27 14:55:29 -05:00
3f28091e52 Tajik translation updated 2014-02-27 12:13:18 +05:00
b4ee86955d messageTray: Make sure that the source actor is square
Commit b7e1539699 removed the size to support hidpi but that caused the
actor to no longer be square. Fix that by going back to setting a size
but apply the scale factor before doing so.
2014-02-26 18:15:38 +01:00
8b866efe92 location: Adapt to new Geoclue agent interface
To be able to correctly setup dbus policy, I had to change the expected
agent D-Bus API a bit: Now object path is fixed and not different for
each user.

https://bugzilla.gnome.org/show_bug.cgi?id=725082
2014-02-25 12:45:32 +00:00
fb61ab8df7 Updated Spanish translation 2014-02-24 17:28:11 +01:00
990956ece7 Updated Galician translations 2014-02-24 15:22:12 +01:00
414b592d53 Updated Hebrew translation 2014-02-24 15:25:37 +02:00
751154d9da shell-util: Fix warning in shell_util_cursor_tracker_to_clutter
If the sprite is NULL, like if a Wayland app wanted to hide the cursor,
then we need to hide the ClutterTexture on our side, as ClutterTexture
has no easy way to tell it to paint nothing.
2014-02-23 10:54:32 -05:00
29addc499c shell-global: Only set the scale factor if get_setting succeeded
If gdk_screen_get_setting fails, like if it's running without XSettings,
then the GValue will have a value of 0. A lot of code tries to divide by
the scale factor. This produces NaN, and combined with the fact that NaN
is "leaky", we very quickly end up spinning out of control.
2014-02-23 10:54:32 -05:00
caa98de581 Updated Hebrew translation 2014-02-23 14:16:30 +02:00
1fd1ec4312 Updated Chinese (China) translation 2014-02-23 09:57:11 +00:00
f4607626e4 workspaceThumbnails: Really fix DND creating new workspaces
The same problem spotted in commit 89a2dc71fc is present in
some more places, fix those as well.

https://bugzilla.gnome.org/show_bug.cgi?id=724686
2014-02-23 00:29:29 +01:00
b494c15e4b shell-js: Kill some compile warnings
jsapi.h has some bad warnings with gcc. gjs-module.h already includes
jsapi.h and uses a complicated set of GCC pragmas to mask them out, so
just kill our include.
2014-02-22 17:42:03 -05:00
3c0defa125 Updated Belarusian translation. 2014-02-22 11:27:32 +01:00
f2df4d95de Updated Czech translation 2014-02-22 08:51:14 +01:00
fabcf20e06 Updated Kazakh translation 2014-02-21 17:25:04 +00:00
b9510b9ab7 Updated Spanish translation 2014-02-21 13:10:31 +01:00
52a2ebad04 Updated Brazilian Portuguese translation 2014-02-21 12:02:52 +00:00
89a2dc71fc workspaceThumbnails: Fix creating new workspace via DND
Commit 61a58ff3c9 replaced the (removed in commit 254afc50223a7)
MetaWindowActor.get_workspace() method by MetaWindow.get_workspace(),
but did not take into account that the return values differ - the
former returns the workspace index, the latter the workspace object.

https://bugzilla.gnome.org/show_bug.cgi?id=724686
2014-02-21 11:48:55 +01:00
adb0de43d8 Updated Indonesian translation 2014-02-21 06:46:22 +00:00
e2a811a720 Updated Greek translation 2014-02-20 23:20:56 +02:00
5bcafc5c17 Updated Lithuanian translation 2014-02-20 22:42:10 +02:00
2d24536caf shellDBus: Fix LaunchExtensionPreferences()
Commit 5dedc5d8ba removed "unused" functionality which was
still used by that method. Launch it via GAppInfo instead, which
still supports passing URIs.

https://bugzilla.gnome.org/show_bug.cgi?id=724813
2014-02-20 20:27:02 +01:00
b088c4086b Tajik translation updated 2014-02-20 17:00:40 +05:00
3c58f4abd3 Bump version to 3.11.90
Update NEWS.
2014-02-20 00:31:55 +01:00
e2a9b27b2b Revert "viewSelector: Give the active page key focus when it is shown"
This broke keyboard navigation in the app picker, which is not an
acceptable regression for the release.

This reverts commit ec2bb039ae.
2014-02-20 00:29:14 +01:00
fcd5f06c09 windowManager: Animate tile previews
With tile previews being implemented as Clutter actors in the shell, we
can now easily add fancy animations when showing/hiding the preview.
Besides looking more polished, the animations also help understanding
what will happen to the window when the drag is finished.

https://bugzilla.gnome.org/show_bug.cgi?id=665758
2014-02-20 00:29:14 +01:00
6d93c8b3fd windowManager: Implement tile previews
Mutter now delegates tile previews to compositor plugins, so
add a simple implementation based on the UI previously provided
by mutter.

https://bugzilla.gnome.org/show_bug.cgi?id=665758
2014-02-20 00:29:14 +01:00
2663e1be5d window-tracker: Be more cautious when setting focus app
Since we started tracking non-interesting windows, we can no longer
assume that if we manage to find an app associated with the focus window,
it should appear focused - we now can find apps for docks, the DESKTOP
window etc.
To restore the old behavior, make sure that the focus window or one of
its parents is "interesting".

https://bugzilla.gnome.org/show_bug.cgi?id=722928
2014-02-20 00:29:14 +01:00
0fa6be4614 endSessionDialog: Add extra strings for translation
Add two extra, currently unused strings in order to make the life easier
for translators and avoid breaking the upcoming string freezes.

https://bugzilla.gnome.org/show_bug.cgi?id=722898
2014-02-20 00:27:35 +01:00
46163a6607 endSessionDialog: Warn when trying to install updates on battery power
Interrupting update installation can mess up the package database quite
a bit and could lead to totally destroying the distro installtion. To
avoid running out of juice during an upgrade, warn when someone tries to
install updates on battery power.

https://bugzilla.gnome.org/show_bug.cgi?id=722898
2014-02-20 00:27:35 +01:00
645ef093f7 endSessionDialog: Offer offline updates in the shutdown dialog
This adds a checkbox for installing software updates to the shut down
dialog. The implementation relies on an already prepared offline update
and uses PackageKit's pk-trigger-offline-update helper to trigger the
installation.

https://bugzilla.gnome.org/show_bug.cgi?id=722898
2014-02-20 00:27:35 +01:00
7551e134da endSessionDialog: Fix the "Restart & Install" button
This moves the dialog type overriding that gnome-software uses to bring
up restartInstallDialogContent from _sync() to OpenAsync(), in order to
ensure the type is overridden early enough to show the correct buttons.

While at this, make sure the & symbol in the button's label is escaped
to avoid runtime warnings, now that the label actually gets used.

https://bugzilla.gnome.org/show_bug.cgi?id=722898
2014-02-20 00:27:35 +01:00
5bec5fb6cb endSessionDialog: Simplify CSS padding handling
... so that we add more items to messageLayout without having to
hardcode the same padding in each of the children.

https://bugzilla.gnome.org/show_bug.cgi?id=722898
2014-02-20 00:27:35 +01:00
c176af4da5 endSessionDialog: Use a 48px icon as per mockups
https://bugzilla.gnome.org/show_bug.cgi?id=722898
2014-02-20 00:27:35 +01:00
2631f03108 windowManager: Don't remove the active workspace
Currently workspaces (except for the last one) are removed when
they become empty. While we do have special treatment for the
case where the currently active workspace is removed, we just
move directly without animations to the last workspace to avoid
ending up on a "random" workspace. However this behavior is still
a bit confusing, so keep the workspace around instead until the
user decides to move to another one.

https://bugzilla.gnome.org/show_bug.cgi?id=709064
2014-02-19 21:52:52 +01:00
525c71658b Updated Norwegian bokmål translation 2014-02-19 19:45:45 +01:00
10e5778deb Updated Brazilian Portuguese translation 2014-02-19 18:04:26 +00:00
6512a5fd6b browser_plugin: Expose version validation boolean
Expose the version validation boolean through the public JS
API so that e.g.o can make use of it.

https://bugzilla.gnome.org/show_bug.cgi?id=724683
2014-02-19 17:07:38 +01:00
1af40b1345 extensionSystem: Add a gsettings key to disable version validation
Add a key 'disable-extension-version-validation' key that disables
the validation of extension's claimed to be supported shell version
with the shell version and just load all extensions unconditionally.

https://bugzilla.gnome.org/show_bug.cgi?id=724683
2014-02-19 17:07:38 +01:00
0418b68051 popupMenu: Use an image for arrows
Until now the arrows were the associated arrow
character of the font. This cause some problems like
different arrows for different fonts, and size can be
altered because of the font size.
To solve that, use an image for the arrows.

https://bugzilla.gnome.org/show_bug.cgi?id=720206
2014-02-19 13:44:27 +01:00
a7283864e8 popupMenu: Use relative rotation for arrows
Currently the animation of the arrows doesn't take
into account previous rotation.
Since in a incoming patch we will use one arrow
and rotate it to generate the four directions, we
need that the animation use relative rotation.

https://bugzilla.gnome.org/show_bug.cgi?id=720206
2014-02-19 13:44:26 +01:00
4950bad2a7 popupMenu: Rename _arrow_rotation to follow JS covention 2014-02-19 13:44:26 +01:00
470ac0eae3 shell_global: Use correct agruments for in update_scale_factor
Otherwise we crash ...

https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-18 23:40:34 +01:00
87abbf9b20 hdpi: Revert hacks
Revert the hacks that where added in response to a bug caused by
commit ba459f4d20

https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-18 23:40:34 +01:00
3e7e88cd5f popupMenu: Make destroy() a bit more robust
Currently destroying a menu's actor and calling its destroy() method
are subtly different - the latter will also result in the menu being
removed from the corresponding menu manager.
There is no good justification for that behavior, so make both actions
behave the same.

https://bugzilla.gnome.org/show_bug.cgi?id=724690
2014-02-18 21:27:24 +01:00
b7e1539699 messageTray: don't force a size on SourceActor's iconBin
Supports hidpi icons this way.

https://bugzilla.gnome.org/show_bug.cgi?id=724607
2014-02-18 07:46:54 -08:00
8492f2ba24 osdWindow: scale by scaleFactor
Since we calculate this dimensions from the size of the monitor, the
result was too large. Scale it by scaleFactor instead.

https://bugzilla.gnome.org/show_bug.cgi?id=724607
2014-02-18 07:46:07 -08:00
737f4eb1c1 dateMenu: Update gnome-clocks' desktop file
https://bugzilla.gnome.org/show_bug.cgi?id=724282
2014-02-18 14:52:48 +01:00
58191ea66b telepathyClient: Use locale format for timestamps
Until now the timestamps were using 24h format.
Check gsetting clock-format to know when
the user is using 12h format or 24h format and
make the timestamp acordingly.

https://bugzilla.gnome.org/show_bug.cgi?id=715158
2014-02-18 11:27:34 +01:00
1f786df462 NetworkMenu: add VPN settings menu item
Add a menu item that launches the VPN page of the network panel,
or the network panel generically if multiple VPNs are configured.

https://bugzilla.gnome.org/show_bug.cgi?id=709167
2014-02-18 00:17:11 +01:00
fa4c481aed telepathyClient: Use ratio char for timestamps 2014-02-17 20:52:51 +01:00
d555fd7883 ShellApp: remove impossible check
The action muxer is created and destroyed with the running state,
it can never be NULL.

https://bugzilla.gnome.org/show_bug.cgi?id=722554
2014-02-17 20:44:58 +01:00
fe7ece1f5a NetworkMenu: use radio items instead of switches for multiple profiles
Multiple connections for the same device are mutually exclusive, so
a switch is not the appropriate UI metaphor. Use a radio item instead,
and provide a separate "Turn off" item to disconnect.
Behavior when there is only one connection is not changed, there
is a single Connect/Turn off item.

https://bugzilla.gnome.org/show_bug.cgi?id=723570
2014-02-17 20:44:58 +01:00
2bb3aed729 NetworkMenu: sort connections by name only
We don't watch for timestamp changes, and sorting by name keeps
the order consistent and predictable. In practice, there should
be at most 3 or 4 elements, so the user will always read all them
at once and sorting is irrelevant.

https://bugzilla.gnome.org/show_bug.cgi?id=723570
2014-02-17 20:44:57 +01:00
488a42696c NetworkMenu: fix connection name update
If the connection name is changed, the UUID doesn't necessarily,
so checkConnection would take the early return path. Make sure
we update the existing menu item too.

https://bugzilla.gnome.org/show_bug.cgi?id=723570
2014-02-17 20:44:57 +01:00
f43ff45683 NetworkMenu: update for latest designs
Distinguish reaching the full internet from being in a non-routed
LAN, and show a different icon when being an hotspot master.

https://bugzilla.gnome.org/show_bug.cgi?id=723570
2014-02-17 20:44:57 +01:00
bde1451896 NMDeviceWired: don't unconditionally access device.active_connection
The active_connection might be null, but the NMApplet code might
still belive the device wrapper is the primary connection (because
dbus signals are emitted one after the other).
(Also, remove an old and wrong comment about bluetooth in wwan code)

Fixes:

(gnome-shell:22511): Gjs-WARNING **: JS ERROR: Exception in callback for signal: icon-changed: TypeError: this._device.active_connection is null
NMDeviceWired<.getIndicatorIcon@resource:///org/gnome/shell/ui/status/network.js:475
wrapper@resource:///org/gnome/gjs/modules/lang.js:169
NMApplet<._updateIcon@resource:///org/gnome/shell/ui/status/network.js:1790
wrapper@resource:///org/gnome/gjs/modules/lang.js:169
_emit@resource:///org/gnome/gjs/modules/signals.js:124
NMConnectionSection<._addConnection/<@resource:///org/gnome/shell/ui/status/network.js:265
_emit@resource:///org/gnome/gjs/modules/signals.js:124
NMConnectionItem<._sync@resource:///org/gnome/shell/ui/status/network.js:137
wrapper@resource:///org/gnome/gjs/modules/lang.js:169
NMConnectionItem<.setActiveConnection@resource:///org/gnome/shell/ui/status/network.js:169
wrapper@resource:///org/gnome/gjs/modules/lang.js:169
NMConnectionDevice<._activeConnectionChanged@resource:///org/gnome/shell/ui/status/network.js:327
wrapper@resource:///org/gnome/gjs/modules/lang.js:169

https://bugzilla.gnome.org/show_bug.cgi?id=723570
2014-02-17 20:44:57 +01:00
fff2ca6f26 telephatyIdle: Time stamps next conversation line
To give more context to the chat notification bubble, put the
time stamp next to the last conversation line instead of
a new line.

https://bugzilla.gnome.org/show_bug.cgi?id=708031
2014-02-17 20:00:00 +01:00
bec57a6cee Revert "location: Allow user to disable GPS"
This reverts commit 5d05b66902.

Had a long discussion with Bastien Nocera and Allan Day on IRC about
this and in the end we decided to go with the simple on/off controls for
now.

Conflicts:
	js/ui/status/location.js

https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-17 15:49:34 +00:00
a012ca4fac layout: Don't query the monitor size twice 2014-02-17 13:42:20 +01:00
3ba49b0a50 layout: Protect against broken monitor size reports
VNC / XRDP reports nonsensial (i.e 0) monitor dimensions causing us to
end up with a dpi of "Infinity" and thus scale even though we shouldn't.

https://bugzilla.redhat.com/show_bug.cgi?id=1065788
2014-02-17 13:38:55 +01:00
314aa024b5 appDisplay: Add time to view switch animation
Add more time to the fade in/out animation when switching
views so they don't switch abruptly and add a delay
between view switch animations to avoid morphing.

https://bugzilla.gnome.org/show_bug.cgi?id=722331
2014-02-17 13:31:52 +01:00
598f750859 messageTray: Add source actor without count label
Split the current implementation of SourceActor into
SourceActor and SourceActorWithLabel.
In this manner we can use source actors withouth count labels,
required in the screenShield to not clash with the count
text label.

https://bugzilla.gnome.org/show_bug.cgi?id=709275
2014-02-17 13:22:39 +01:00
8057848458 location: Rename _onoffAction to _onOffAction
It was just a typo that this patch now corrects.

https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-17 12:08:40 +00:00
e80c28a530 location: Mark 2 user-visible strings for translation
https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-17 12:08:40 +00:00
5d05b66902 location: Allow user to disable GPS
When exact accuracy (i-e GPS) is available, allow user to disable that.
When users don't want application to get their precise location, they
can now opt for network-based geolocation only, which can be
street-level at best.

https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-17 12:08:40 +00:00
59634b2cf5 location: Rename _updateMenuVisibility to _updateMenu
We'll soon be doing more than just toggling the accuracy of menu itself
in this function. Hence the renaming.

https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-17 12:08:40 +00:00
7c3892f5a2 location: disconnect from 'g-properties-changed' signal
https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-17 12:08:40 +00:00
19406a238b location: 'MaxAccuracyLevel' property is now read-only
https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-17 12:08:40 +00:00
d6146197dd location,schema: Keep max accuracy in gsetting
Instead of relying on geoclue to store this user configuration, lets
keep it in gsettings. Geoclue is a system service and therefore is not
the appropriate entity to keep this info.

https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-17 12:08:39 +00:00
38f241479c location: Coding style fixes
No space between identifier and '('.

https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-17 12:08:39 +00:00
aa45999824 location: Rename _setAccuracy to setMaxAccuracyLevel
More descriptive name to avoid any confusion.

https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-17 12:08:39 +00:00
3b7593ed7f altTab: Scale thumbnails by the scale factor
Fixes their size on high dpi.

https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-16 16:55:55 +01:00
f959cafb36 appDisplay: don't force size on folder icon actor
Use a table layout instead.

https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-16 07:49:25 -08:00
e92d204d42 dash: account for scale factor to determine icon size
https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-16 07:49:25 -08:00
f543161234 st-icon: remove custom size request/allocate
Use a layout manager instead. This has the effect of not enforcing a
priv->icon_size size request, since that value is unscaled.

https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-16 07:49:25 -08:00
9cc1017912 iconGrid: don't force icon size to the BaseIcon
Since we might get a scaled up version for HiDpi.

https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-16 07:49:25 -08:00
fc719c19f9 altTab: account for scale factor when computing icon size
https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-16 07:49:24 -08:00
ad97fc6855 altTab: don't override icon size request
Instead of overriding the actor's request with the icon size, just set
the new icon size on the actors, and let the default handler take the
preferred size of children.

https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-16 07:49:24 -08:00
407dc74502 shell-app: finish support for loading with scale
https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-16 07:49:24 -08:00
e5e764b402 texture-cache: require icon scale to load gicon
To support HiDpi.

https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-16 07:49:24 -08:00
65ad65fe52 Updated Brazilian Portuguese translation 2014-02-16 06:23:41 +00:00
8d09d20510 Updated Hebrew translation 2014-02-15 20:16:43 +02:00
5a5b04b2b0 Updated POTFILES.in 2014-02-15 20:13:20 +02:00
3113bac8e6 location: React to changes in available accuracy level
GPS could be plugged in and out, network can go up and down and
therefore available accuracy level can also change.

https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-15 17:41:26 +00:00
9217f2c916 location: Don't show menu if geoclue is totally incapable
If geoclue can't provide location at all (no GPS or network), don't
bother to show controls that don't serve any purpose.

https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-15 17:41:26 +00:00
32a49b7846 location: Provide a way to disable geolocation
Now that we are indicating 'geolocation in use' to user, we better also
provide at least a way to disable geolocation. Once this is in place, we
can provide slightly better controls rather than simply on/off switch.

https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-15 17:41:26 +00:00
12ef034b7b location: Make _connectToGeoclue() reentrent
https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-15 17:41:26 +00:00
e70e4a21f2 location: Rename _onGeoclueAppeared to _connectToGeoclue
Rename _onGeoclueAppeared to _connectToGeoclue as in the following patch
we'll call it from other contexts then geoclue appearing.

https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-15 17:41:26 +00:00
7826fb4f04 location: Create Geoclue proxy asynchronously
https://bugzilla.gnome.org/show_bug.cgi?id=723684
2014-02-15 17:41:26 +00:00
8f1b8909dc Fix position/size changed disconnects
Commit 3f7a989d38 changed the signals to match mutter api changes,
but did not update disconnects, so do that.
2014-02-15 13:07:14 +01:00
3f7a989d38 Update to changes for MetaWindow size-changed / position-changed signals
These have changed upstream to be on the MetaWindow, rather than
the MetaWindowActor. Follow suit.
2014-02-14 23:49:54 -05:00
4d3fd7598d appDisplay: Re-add _getCategories
It was introduced by one of the commits reverted by 812a61939e,
but is now used for app folders as well; add it back.
2014-02-14 14:02:31 +01:00
620e3cef20 appDisplay: Open new window on middle-click
The current middle-click action of opening a new window on a new
workspace is a bit peculiar; it is also not overly hard to achieve
the same result by moving a new window to the desired workspace or
selecting a workspace before opening a new window. Just opening
a new window is also a more common action, so having a shortcut
available that doesn't require a modifier is a good idea as well;
change the middle-click behavior accordingly.

https://bugzilla.gnome.org/show_bug.cgi?id=695010
2014-02-14 12:01:48 +01:00
812a61939e Revert "appDisplay: Special case terminal launching"
Having terminal launchers behave differently than any other launchers
is non-obvious and confusing. Remove the special-casing to restore
consistency, we will make the new window action more accessible
instead.
This reverts commit 68faba6bde, 4ed0f3e5f0
and 887a21afb9.

https://bugzilla.gnome.org/show_bug.cgi?id=695010
2014-02-14 12:01:48 +01:00
793c6c2f7b Magnifier: clip the UI group clone to the allocation
Without clipping, we show parts of the message tray and workspace
thumbnails that are normally clipped by the screen.

https://bugzilla.gnome.org/show_bug.cgi?id=724305
2014-02-13 19:43:44 +01:00
f8f4d0f646 Magnifier: use the system noise texture for the dead area
The design says that the noise texture is the implicit bottom
layer, and so it's appropriate to use it togheter with the default
color to cover the dead area outside the screen which becomes
visibile when scrolling.

https://bugzilla.gnome.org/show_bug.cgi?id=724305
2014-02-13 19:43:44 +01:00
3a92aa751f Magnifier: don't listen for focus/tracker events if the magnifier is not active
In addition to checking the current settings, check also if the
zoom region is active before registering the event listener.
This way, we avoid DBus traffic for events we're not interested in.

Also, make FocusCaretTracker resilient to multiple register/deregister
calls (which can now happen).

https://bugzilla.gnome.org/show_bug.cgi?id=724305
2014-02-13 19:43:41 +01:00
6882273aa0 Magnifier: demote exceptions reading focus/caret position
It's possible that the DBus call goes in timeout (which is bad,
but unavoidable, given that atspi is synchronous) or fails
because the component has been removed (race condition). Those
errors are not dependent on gnome-shell, but on faulty applications
(mostly).
If they happen, log a debug message and continue.

https://bugzilla.gnome.org/show_bug.cgi?id=724305
2014-02-13 19:31:00 +01:00
3b0197620f Magnifier: fix a warning when calling setActive() with the same value
We must not call enable/disable_unredirect if we didn't change the
active value, otherwise mutter gets confused and emits warnings.

https://bugzilla.gnome.org/show_bug.cgi?id=724293
2014-02-13 19:31:00 +01:00
f6240e114c Don't send all debug messages to Telepathy
The log handler can be invoked at bad times, and in particular
it can be invoked from gsignal with the signal lock taken.
At that time, calling into arbitrary high-level APIs can
cause a dead-lock.
Instead, only send to telepathy the tp-glib debug messages.
Everything else is in the journal anyway.

https://bugzilla.gnome.org/show_bug.cgi?id=724256
2014-02-13 13:03:37 +01:00
0f3c129b95 layout: Set high dpi scaling factor
Set the scaling factor when the dpi is above the hidpi threshold.

https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-12 22:53:37 +01:00
6f87b01c47 Revert "shell-global: Set high dpi scaling factor"
This reverts commit ba459f4d20.

This breaks gdm.
2014-02-12 22:53:11 +01:00
32110a9866 layout: Remove stray reference to parentSetId
This was removed in a4dea25d76.
2014-02-12 14:13:08 -05:00
ba459f4d20 shell-global: Set high dpi scaling factor
Set the scaling factor based on the xsetting.

https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-12 19:45:53 +01:00
d868e6bfaf st: Add high dpi support
Add a scale_factor property to StThemeContext that can
be used to enable (integer) scaling of pixel values.

https://bugzilla.gnome.org/show_bug.cgi?id=705410
2014-02-12 19:45:47 +01:00
9f3499a7c3 make NetworkManager optional
NetworkManager is only available on Linux.

https://bugzilla.gnome.org/show_bug.cgi?id=669495
2014-02-11 18:04:44 -05:00
ccec7732a7 calendar server: fix _LDFLAGS vs _LDADD mixup
_LDFLAGS is for flags.  _LDADD is for LIBS.

https://bugzilla.gnome.org/show_bug.cgi?id=724184
2014-02-11 17:46:44 -05:00
3b980a173f Updated Assamese translation 2014-02-11 13:07:53 +00:00
246139f90b Updated Ukranian 2014-02-09 23:08:26 +02:00
4e85fb7d8d Updated German translation 2014-02-09 20:17:24 +00:00
ab32411b0c Updated Norwegian bokmål translation 2014-02-09 19:46:37 +01:00
477f28a6bd LayoutManager: untrack actors that are destroyed
If an actor is destroyed, calling get_transformed_size()/position()
can return bogus values and trigger JS exceptions.
This can happen if a tracked actor comes from an extension
(such as classic mode's window-list) and that extension is
deactivated.

https://bugzilla.gnome.org/show_bug.cgi?id=723661
2014-02-09 19:30:02 +01:00
96ef0a178d autogen.sh: use #!/bin/sh instead of #!/bin/bash
We don't have any bashisms in this file, so we may as well use sh.

https://bugzilla.gnome.org/show_bug.cgi?id=722342
2014-02-09 11:00:04 -05:00
ab603e7ef7 Updated Aragonese translation 2014-02-09 13:25:09 +01:00
d52104a62a Updated Kazakh translation 2014-02-09 04:11:08 +00:00
8d8d1cfdd6 Magnifier: Restore crosshairs
This patch is to restore broken crosshairs so they may be used
once more

https://bugzilla.gnome.org/show_bug.cgi?id=723709
	magnifier.js
2014-02-08 23:47:40 +00:00
5451751513 Updated Czech translation 2014-02-08 11:22:36 +01:00
92ae26bb9f Fix username shadows in Lock/Loginscreen
https://bugzilla.gnome.org/show_bug.cgi?id=723833
2014-02-07 16:20:14 +00:00
3c8ee0c8cb Updated Spanish translation 2014-02-07 14:47:46 +01:00
20f76b8118 Tajik translation updated 2014-02-07 09:59:45 +05:00
d8eeeead18 Updated Galician translations 2014-02-07 00:59:18 +01:00
5452162bc3 Updated Brazilian Portuguese translation 2014-02-06 18:53:18 +00:00
a4adcba405 Revert "network: Use NMDevice.get_available_connections(); for NMWirelessDialog"
This reverts commit a36bfced47.

get_available_connections() returns the connections that are
available right now, ie, with the currently visible APs, but
that might change while the dialog is open, so we can't use it.

https://bugzilla.gnome.org/show_bug.cgi?id=709128
2014-02-06 19:45:24 +01:00
66da594382 Updated Hebrew translation 2014-02-06 20:30:25 +02:00
aa426842f2 BluetoothMenu: show the submenu when active but not connected
Following the updated designs for system status, show the submenu
but not the indicator when bluetooth radio is on but no device
is connected.

https://bugzilla.gnome.org/show_bug.cgi?id=709353
2014-02-06 19:07:48 +01:00
ed53a45228 NetworkMenu: show the device name instead of Bluetooth
To distinguish the bluetooth network menu from the bluetooth
menu proper, use the device name as the label.
Also, replace Connect with "Use as Internet connection"

https://bugzilla.gnome.org/show_bug.cgi?id=709353
2014-02-06 19:07:47 +01:00
2ddbcb2369 Updated Brazilian Portuguese translation 2014-02-06 17:35:33 +00:00
61a58ff3c9 Fix regression after mutter commit 254afc50223a707c3afa7c9f638681199f41809e
The "old and unused API" was not so unused after all :)
2014-02-06 18:15:36 +01:00
638aee65c0 Update Arabic translation 2014-02-06 15:32:37 +02:00
f21c49f8da Slider: put imports in order
Bug https://bugzilla.gnome.org/show_bug.cgi?id=712649
2014-02-05 19:36:50 +00:00
583d2cb4e4 Magnifier: take x,y from center of focused widget
bug https://bugzilla.gnome.org/show_bug.cgi?id=720951
2014-02-05 18:21:42 +00:00
aa70dcfc8f Updated Spanish translation 2014-02-05 13:48:04 +01:00
ffb61c425b Tajik translation updated 2014-02-05 10:59:16 +05:00
858cf5e0c9 Bump version to 3.11.5
Update NEWS.
2014-02-05 00:17:32 +01:00
881dd4666e Updated Hebrew translation
Signed-off-by: Yosef Or Boczko <yoseforb@src.gnome.org>
2014-02-05 00:32:33 +02:00
c4aeaf7fe8 NetworkMenu: use the carrier state to the decide if wired should be shown
The design says "when a network cable is connected", not
"When a network cable is connected and the link is up and we
have an IP etc. etc." (which is what ACTIVATED would imply).

https://bugzilla.gnome.org/show_bug.cgi?id=723570
2014-02-04 22:25:11 +01:00
ea1f5a8fc6 NMWirelessDialog: show an informative message when airplane mode is on
Rather than just showing "No networks", inform the user if airplane
mode is on or if wifi is off, and allow the user to change that
from the dialog (if possible).

https://bugzilla.gnome.org/show_bug.cgi?id=709128
2014-02-04 22:25:06 +01:00
7f1e420a0a NMWirelessDialog: request a scan when there are no visible access points
Ideally, we should keep scanning while the dialog is open, and
stop (or reduce the frequency) when it's closed. Forcing a new
scan when the dialog is opened empty is a close approximation
and increases the probability that the user will find the AP
he needs.

https://bugzilla.gnome.org/show_bug.cgi?id=709128
2014-02-04 22:24:01 +01:00
1272eaf07f NMWirelessDialog: fix removing access points
We must destroy the actor, not the item, because the latter doesn't
have a destroy() method.

https://bugzilla.gnome.org/show_bug.cgi?id=709128
2014-02-04 22:24:00 +01:00
2fe760cc4b rfkill: split out the dbus handling parts from the indicator
status.network wants to watch for airplane mode too, we can
share the code with a manager singleton.

https://bugzilla.gnome.org/show_bug.cgi?id=709128
2014-02-04 22:24:00 +01:00
df3a50bae8 AirplaneMode: don't show a "Turn off" item if HW enabled
If airplane mode is enabled with an HW switch, we can't turn it
off from the menu, so make the menu item insensitive and
replace it with a helpful tip.

https://bugzilla.gnome.org/show_bug.cgi?id=709685
2014-02-04 22:24:00 +01:00
0db3605f33 Magnifier: Disable unredirect when active
The unredirect feature does not apply to the magnifier and it
prevents users from gaming whilst it is on so disable when magnifier
is active and allow magnifier users to game!

Bug https://bugzilla.gnome.org/show_bug.cgi?id=708985
2014-02-04 16:58:25 +01:00
173fa92116 Updated Indonesian translation 2014-02-04 14:16:26 +00:00
02ca58c1eb Updated Hebrew translation. 2014-02-03 16:30:03 +02:00
4a22fe58bf updated kn.po 2014-02-03 16:51:29 +05:30
3fc478a14b overview: don't add a null desktop clone
If there is no desktop window, getDesktopClone() returns null.
In that case, we know that no animation is needed.

Fixes a regression from b97f3a9ecf

https://bugzilla.gnome.org/show_bug.cgi?id=723523
2014-02-03 11:55:50 +01:00
203bc674fe Updated Traditional Chinese translation(Hong Kong and Taiwan) 2014-02-02 20:36:23 +08:00
a4a9f0a04c Updated Kazakh translation 2014-02-01 15:12:11 +00:00
918e7fffeb Updated Tajik translation 2014-02-01 12:55:49 +00:00
e1648de661 Updated Brazilian Portuguese translation 2014-02-01 10:05:14 +00:00
f7c94e6343 Updated Greek translation 2014-02-01 08:16:19 +02:00
2fba8e29e0 shell-app: Rename internal function as suggested in review
Ooops, this was meant to be squashed with commit d44f40d105.
2014-01-31 13:59:30 +01:00
de1bb4e203 window-tracker: Remove is_window_interesting()
It is now unused, so kill it.

https://bugzilla.gnome.org/show_bug.cgi?id=723308
2014-01-31 13:49:07 +01:00
b4680a5c25 Use meta_window_is_skip_taskbar() directly
Rather than going through ShellWindowTracker, we can just clean up
the code to use the underlying MetaWindow property directly.

https://bugzilla.gnome.org/show_bug.cgi?id=723308
2014-01-31 13:49:07 +01:00
d44f40d105 shell-app: Track changes to MetaWindow:skip-taskbar
So far we have assumed that whether or not a window is interesting
is static. In general this is the case, but as it is legal for the
underlying properties to change at any time, there are of course
offenders that actually do this (flash I'm looking at ya).
While we used the property to determine whether a window should be
tracked or not, the worst case was showing windows that should be
hidden or missing windows that should be shown.
However as we nowadays base an app's running state on the number of
interesting windows, we need to be more careful in order to avoid
ending up with running apps with no windows.

https://bugzilla.gnome.org/show_bug.cgi?id=723308
2014-01-31 13:49:07 +01:00
8d5771e302 window-tracker: Use MetaWindow:skip-taskbar
The code from shell_window_tracker_is_window_interesting() is equivalent
of MetaWindow's skip-taskbar property, so use it to avoid code duplication.

https://bugzilla.gnome.org/show_bug.cgi?id=723308
2014-01-31 13:49:06 +01:00
fb31f99aed Updated Aragonese translation 2014-01-31 12:45:29 +01:00
b97f3a9ecf overview: Fix DESKTOP clone position
The DESKTOP window might not be located at (0,0), in particular
on multi-monitor setups. While we already consider this by setting
the clone's position, we then stuff the clone into a container which
ignores it - meh ...

https://bugzilla.gnome.org/show_bug.cgi?id=723306
2014-01-31 00:00:07 +01:00
ac22172a6e window-tracker: Fix memory leak
shell_window_tracker_get_window_app() returns a new reference
which we need to drop once we're done.

https://bugzilla.gnome.org/show_bug.cgi?id=722927
2014-01-30 23:55:13 +01:00
57367380f5 Updated Hebrew translation 2014-01-29 22:39:47 +02:00
7101cc3170 osdWindow: Don't tween to undefined
Check that we got a valid level before setting the level bar to it, to
prevent a Tweener warning.
2014-01-29 15:08:03 -05:00
7051411be7 network: Add a Wired device
This isn't quite like the design, as we don't show icons for other
devices when wired is in an error state.

https://bugzilla.gnome.org/show_bug.cgi?id=708966
2014-01-29 15:02:52 -05:00
bb8397b9b1 appDisplay: Support the 'excluded-apps' key for app folders
https://bugzilla.gnome.org/show_bug.cgi?id=723179
2014-01-29 14:02:27 -05:00
bb8fa61cb4 appDisplay: Support the 'categories' key for app folders
https://bugzilla.gnome.org/show_bug.cgi?id=723179
2014-01-29 14:02:27 -05:00
10147ee331 appDisplay: Make refiltering folders more efficient
Rather than queueing a full redisplay, simply filter the apps inside
folders by toggling the icon's visibility.

https://bugzilla.gnome.org/show_bug.cgi?id=723179
2014-01-29 14:02:27 -05:00
3779ac2c8a appDisplay: Make AllView handle the display of the all apps view
Rather than having the central component handle it. Just a code tidying-up.

https://bugzilla.gnome.org/show_bug.cgi?id=723179
2014-01-29 14:02:27 -05:00
bdad4db9ec appDisplay: Only reload frequently used data when mapping the frequent view
If the user mostly uses the All Apps view and uses it as his default view,
we shouldn't reload frequent data after a timeout. Simply do it when the
view is mapped.

https://bugzilla.gnome.org/show_bug.cgi?id=723179
2014-01-29 14:02:26 -05:00
36c69124f7 shell-app: Don't crash when trying to dispose
If we dispose a ShellApp that has windows left, then we'll crash
when we remove the last window, as that frees and NULLs out
app->running_state.

https://bugzilla.gnome.org/show_bug.cgi?id=723197
2014-01-29 14:02:26 -05:00
7c8c811134 Dispose cairo contexts in osdWindow and screenShield
Need to manually dispose of cairo contexts used in gjs with $dispose(),
or the context object will leak. These classes used cairo for drawing but
were missing the dispose call.

https://bugzilla.gnome.org/show_bug.cgi?id=722812
2014-01-28 16:46:10 -08:00
ccfc9f3ab0 TelepathyClient: disconnect signals from destroyed ChatSources
Otherwise they keep firing if the source is destroyed by the user
and they trigger Clutter warnings from touching invalid actors.

https://bugzilla.gnome.org/show_bug.cgi?id=722660
2014-01-28 21:50:07 +01:00
d163b92e0b Add indicator for location service being used
If an application is accessing location through geoclue, show an
indicator in the panel for that so that user knows.

https://bugzilla.gnome.org/show_bug.cgi?id=709372
2014-01-28 18:39:12 +00:00
887a21afb9 appDisplay: Properly check for TerminalEmulator in the Categories key
get_categories() returns an unparsed string, not a list of categories.
We need to parse the list by splitting on ';' to deterine whether the
actual 'TerminalEmulator' category is in the list, rather than something
like 'X-GNOME-TerminalEmulator'. It's an edge case, but I need to split
the list properly for the new folders, so I might as well fix this.

https://bugzilla.gnome.org/show_bug.cgi?id=723179
2014-01-28 13:32:36 -05:00
634adc9f71 Fixed Russian translation 2014-01-28 22:18:22 +04:00
974f01cef7 appDisplay: Remove unused method 2014-01-28 11:17:34 -05:00
ef9ade3548 shell-global: Remove unused declaration 2014-01-28 11:17:34 -05:00
24fdb73b44 Updated Lithuanian translation 2014-01-25 22:17:09 +02:00
ba06a87ba8 window-tracker: Fix typo 2014-01-24 12:38:27 -05:00
e6be483755 network: Fix copy-paste typo 2014-01-23 16:11:11 -05:00
a36bfced47 network: Use NMDevice.get_available_connections(); for NMWirelessDialog
It's a lot simpler and doesn't require us routing the NMRemoteSettings
all the way through. It's still a bit complicated to do this for the
usual connections, so let's drop it for now.
2014-01-23 16:02:19 -05:00
4faf421d5a network: Let NetworkManager figure out the best AP for the connection
According to dcbw, this can be handled by NetworkManager now.
2014-01-23 14:38:40 -05:00
a739455414 gnome-shell-extension-prefs: Fix warnings 2014-01-23 14:38:31 -05:00
73f6e75d8d shell-app: Unref running state when window count drops to zero
With the lastest ShellApp changes, an app is considered stopped
when the last "interesting" window is closed. However the app
may still track non-interesting windows, so if we unref the
running state on the state transition, we hit an assertion later-on
when trying to remove the non-interesting window.
Fix this by keeping the running state around until the last window
is closed.

https://bugzilla.gnome.org/show_bug.cgi?id=722840
2014-01-23 12:45:23 -05:00
17e7f8057a Assamese translation updated 2014-01-23 19:06:34 +05:30
b62c157680 shell-app: Base running state on "interesting" windows
An app should be considered running if it has at least one "interesting"
window, however the code considers an app running if it has at least
one tracked window. This was fine while we were only tracking interesting
windows, but since commit d21aa0d85f this is no longer the case.
So keep track of the number of interesting windows as well and use that
to determine the running state.

https://bugzilla.gnome.org/show_bug.cgi?id=722690
2014-01-22 22:16:03 +01:00
816f5162f9 BackgroundManager: don't destroy the newly created actor
Because we were setting this.background before calling .destroy(),
the call that was meant to destroy the old background was actually
destroying the new one!

https://bugzilla.gnome.org/show_bug.cgi?id=722787
2014-01-22 20:35:02 +01:00
9d8f8277aa altTab: Always filter out items with no windows
When restricting the switcher popup to the current workspace, we
filter out running apps with an empty window list (namely: no open
windows on the current workspace). However we may end up with an
empty window list even when not restricting items to the current
workspace when all windows of a running app are associated with a
different application via the transient_for hint.
To fix this, just filter out items with an empty window list
unconditionally.

https://bugzilla.gnome.org/show_bug.cgi?id=722434
2014-01-22 13:52:49 -05:00
cca14053a4 window-tracker: Always enable transient_for redirection
It is possible to associate an application's window with a different
application using the transient_for hint. However we currently only
consider the hint in get_window_app() and not when making the original
association, which opens the door to some confusing inconsistencies;
for instance, get_window_app() will not necessarily return the same
value for all windows retrieved via shell_app_get_windows().
Fix this by looking at the transient_for hint when making the original
association, not just in get_window_app().

https://bugzilla.gnome.org/show_bug.cgi?id=722434
2014-01-22 13:52:49 -05:00
427790f005 switcherPopup: Fix spacing calculation for empty lists
Without special-casing, our current spacing calculation results in
negative size requests for empty lists, which will trigger a Clutter
assert later.
While the list is never supposed to be empty, bugs happen; crashing
users' systems is the least graceful way of handling this, so don't
do it.

https://bugzilla.gnome.org/show_bug.cgi?id=722434
2014-01-22 13:52:49 -05:00
17845bf71e Updated Norwegian bokmål translation 2014-01-21 20:03:49 +01:00
452f5ab3ba Updated Galician translations 2014-01-20 23:10:20 +01:00
335744e78a build: bump clutter requirement to 1.15.90
PopupMenus don't render properly with clutter older than this.

https://bugzilla.gnome.org/show_bug.cgi?id=722593
2014-01-21 08:03:29 +11:00
c9e24439b2 Updated Spanish translation 2014-01-20 16:39:10 +01:00
61c697b6db Updated Brazilian Portuguese translation 2014-01-19 21:12:15 -02:00
3227d4f3ed ShellApp+ShellGlobal: unify app launch context code
Extend shell_global_create_app_launch_context() with the required
parameters and use that for shell_app_launch() too.

https://bugzilla.gnome.org/show_bug.cgi?id=669603
2014-01-19 18:51:48 +01:00
7e27afb645 Introduce support for desktop actions in the dash
Using the new list_actions() API in Gio, add entries for static
actions specified in .desktop files in the right-click app menus,
in the dash, app well and search.

https://bugzilla.gnome.org/show_bug.cgi?id=669603
2014-01-19 18:47:29 +01:00
9ba4790b4d AppDisplay: clean up handling of right click popup menu
Instead of connecting a global activate handler on the menu,
have one on each menu item, individually doing the right thing.

https://bugzilla.gnome.org/show_bug.cgi?id=669603
2014-01-19 18:46:31 +01:00
3a26f7f4d5 WindowManager: WORKAROUND: disable dimming in the overview
Clones and effects don't mix, due to a bug in Clutter we don't
know how to fix, and sometimes the clone is clipped completely.
As a workaround, undim windows with dialogs in the overview.

Clutter bug: https://bugzilla.gnome.org/show_bug.cgi?id=659523

https://bugzilla.gnome.org/show_bug.cgi?id=650843
2014-01-19 16:38:19 +01:00
8b99617513 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
2014-01-19 16:37:17 +01:00
587655f063 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
2014-01-19 16:37:17 +01:00
7e9ecf4eb2 Add a radial background shade for modal dialogs
Use a new ShellGLSLQuad actor class to build a RadialEffect that can be
enabled on Lightboxes to achieve a radial effect similar to the overview
one. Then enable it for modal dialogs.

https://bugzilla.gnome.org/show_bug.cgi?id=669798
2014-01-19 16:02:46 +01:00
5413010c60 Notification: don't expand the content on a destroyed notification
If the notification is destroyed between an allocate and the redraw,
the meta_later is invoked on a destroyed object, and fails because
the clutter calls are invalid at that point.

https://bugzilla.gnome.org/show_bug.cgi?id=722547
2014-01-19 15:59:18 +01:00
7d13cf1587 ShellTrayIcon: forward key presses too
StWidget::popup-menu is emitted when Menu/<Shift>F10 is pressed,
not released (for consistency with Gtk+), so we need to forward
that. Note that for key press we don't emit the matching key
release, because the app will take a grab and get the event directly
from X when the key is physicall released.

https://bugzilla.gnome.org/show_bug.cgi?id=721267
2014-01-19 15:42:54 +01:00
24cd13935a build: Don't dist generated gresources files
In addition to 2dd7db4808
2014-01-19 10:15:15 +01:00
1ae7dbec67 build: Fix linker failure for gnome-shell-extension-prefs
In addition to 2dd7db4808
2014-01-19 10:08:42 +01:00
2b0a2ab3bc build tools: Update to 3.12 2014-01-18 19:33:49 +01:00
4ed0f3e5f0 AppDisplay: fix isTerminal() check for apps without .desktop files
If the app has no .desktop file get_app_info() returns null.
This breaks window switching using the dash for those apps.

https://bugzilla.gnome.org/show_bug.cgi?id=722494
2014-01-18 11:36:10 -05:00
9cacc703dd Tajik translation updated 2014-01-18 16:48:41 +05:00
9ae70c6519 Updated Czech translation 2014-01-18 12:23:10 +01:00
02b38fed49 Update Chinese simplified translation 2014-01-18 16:48:13 +08:00
b2a65f809f Use recommended quotes
See https://wiki.gnome.org/Design/OS/Typography
2014-01-17 16:34:44 -05:00
2931869522 [l10n] Updated Estonian translation 2014-01-17 17:44:49 +02:00
11d8640ba6 workspacesView: Fix activating empty workspaces
By default, gesture actions no longer wait for the dnd threshold to
be reached before triggering, which breaks our mixing of click- and
pan actions. Fix this by only panning after reaching the threshold
and letting the click action go through if the pan is cancelled.

https://bugzilla.gnome.org/show_bug.cgi?id=722417
2014-01-17 09:30:59 -05:00
65ff947b5e Updated Czech translation 2014-01-17 14:21:41 +01:00
2dd7db4808 Make gnome-shell-extension-prefs a binary executable
Since commit 1ebb162a00 moved JS sources into resources,
the extension-prefs tool was broken. To fix it, we would either
need to generate an external GResource in addition to the generated
C code and teach gjs-console about loading it before evaluating
the script, or turn gnome-shell-extension-prefs into a binary with
the JS resources compiled in.

https://bugzilla.gnome.org/show_bug.cgi?id=722334
2014-01-16 09:33:01 -05:00
1d7354696e Bump version to 3.11.4
Update NEWS.
2014-01-16 01:52:54 +01:00
cbceac4c8a boxpointer: Add condition checks for -arrow-rise:0px
This is done for properly drawing popup menu when arrow rise is 0 (in
case of background menu).

Normally, the menu with arrow rise set to 0 is drawn properly having
all four corners rounded. But when the source(click/arrowOrigin) is
near screen's edges, one of the corners (depending on source's position
and arrow alignment) is drawn right angled.

This happens because the rounded corners are skipped and right angled
arrow is drawn when arrow origin is close to the edges.(That's why when
arrow-rise is 0, it forms right angled corner).

So, a few condition checks are made to ensure that right angled corner
is not drawn.

https://bugzilla.gnome.org/show_bug.cgi?id=699608
2014-01-15 18:28:31 +01:00
297877fbe2 theme: Set -arrow-rise property of backgroundMenu to 0
Background menu shown on right clicking desktop background has an arrow
pointer which points to nothing. This patch sets its height (rise) to 0
so that no arrow is formed.

https://bugzilla.gnome.org/show_bug.cgi?id=699608
2014-01-15 18:28:31 +01:00
0d92451c49 shell-global: Remove unused gc-notifications support
This is going to be removed upstream.
2014-01-15 09:18:53 -05:00
c8a58dcb69 layout: Add a standard dummy cursor
Right now we have three "dummy cursor" widgets between the background
menu, the message tray menu, and the IBus candidate popup. Consolidate
these into one "dummy cursor" widget which is tracked in the layout
manager.
2014-01-14 18:56:45 -05:00
a4dea25d76 layout: Don't require fullscreen-tracked actors to be toplevel
I don't know why this was ever here; We only have one
fullscreen-tracked actor.
2014-01-14 18:56:45 -05:00
bfb0235fc6 Remove our custom hashmap implementation
gjs uses Spidermonkey 24, which implements Map from the ES6
specification, so we can use that instead.

https://bugzilla.gnome.org/show_bug.cgi?id=722210
2014-01-15 00:55:00 +01:00
6dcc3d637f windowManager: Fix the name of the keybinding 2014-01-14 17:58:51 -05:00
9bb4d17e31 Add a debug keybinding to pause/resume all tweens
So we can inspect a mid-tween scene easier.
2014-01-14 17:25:14 -05:00
9df09db5fe appDisplay: Use the new org.gnome.desktop.app-folders schema for grouping
Rather than GMenu / app-folder-categories. This removes our last use of
gnome-menus in the stock gnome-shell, which is exciting, but also means
that app folders in Software start working.

Ideally, we'd have a button to launch our Software app as well from the
overview.

https://bugzilla.gnome.org/show_bug.cgi?id=722117
2014-01-14 11:25:08 -05:00
d8e28ec274 viewSelector: Remove unused setActivePage 2014-01-14 09:10:12 -05:00
d3905734c1 background: Remove unused argument 2014-01-13 20:17:57 -05:00
8fe7f923ec appDisplay: Move from instanceof-testing to polymorphic duck typing
Instead of having _compareItems, _getItemId, etc. on the view to
pull out info about items, use the AppIcon / FolderIcon items we
create as a place to track this additional info. We now require
that these items have a '.id' property for deduplication, and a
'.name' property to sort by.

https://bugzilla.gnome.org/show_bug.cgi?id=722117
2014-01-13 17:28:32 -05:00
933f38390b background: Don't require passing in a background to _updateBackground()
To make debugging background issues easier.
2014-01-13 17:14:58 -05:00
a4e019442f AppDisplay: fix isTerminal() check for apps without categories
If there is no Categories line in the .desktop file, get_categories()
returns null, not an empty array.
2014-01-13 23:13:28 +01:00
d1c4e60636 switcherPopup: Always show the arrows if the popup is scrollable
Currently we only show/hide the left and right arrows when
we reach the initial/end position. This patch changes this
behaviour by showing the arrows whenever is a scroll is possible.

https://bugzilla.gnome.org/show_bug.cgi?id=711467
2014-01-13 16:02:35 -02:00
765d0228c0 loginDialog: move user list loading after actors are constructed
Right now we queue populating the user list in the middle of setting
up the dialog actors. Of course, the actual population happens some time
later after going back to the main loop.

It's more logical to structure the code so the the actors are
instantiated first in one block and then other things after that.

This commit moves the user list population enqueuing operation to the
bottom of the constuctor.

https://bugzilla.gnome.org/show_bug.cgi?id=721868
2014-01-13 12:42:39 -05:00
2d2020a20d loginDialog: defer loading user list until idle
In some cases we load the user list after going back
to main loop and in other cases we load the user list
right away (depending on if accounts service is ready).

In the case we load the user list right away we cause a
traceback because loading the user list forces a reset,
which then tries to reset actors which aren't instantiated
yet.

This commit ensures the user list is loaded after the constructor
finishes and the event loop runs irregardless of the accountsservice
state.

https://bugzilla.gnome.org/show_bug.cgi?id=721868
2014-01-13 12:42:33 -05:00
03ab282f67 appDisplay: Fix style 2014-01-13 11:39:30 -05:00
7f94cb1cad Updated Galician translations 2014-01-13 01:46:07 +01:00
a2a303bd72 Updated Greek translation 2014-01-12 19:53:24 +02:00
d34bf9a14d app-system: Remove unnecessary debugging statement
It's just spam.
2014-01-10 15:19:07 -05:00
68faba6bde appDisplay: Special case terminal launching
One of the most frequent complaints about our launching behaviour is
how we handle terminals. Among all MDI applications, the terminal is
the one that is most likely to have lots of semi-independent windows
opened at the same time, and spawning new windows is much more common.
More so, if it does not support tabs.

Therefore, we special case terminal launchers to always create a new
window. It is an application that most non-technical users will not
use, so chances of them being confused by any special behaviour is
expected to be low.

https://bugzilla.gnome.org/show_bug.cgi?id=695010
2014-01-10 12:08:14 +01:00
5c5b9cfd96 Also update gtkaction*
Forgot about this.
2014-01-09 14:59:31 -05:00
9d683f4767 gtkmenutracker: Update from GTK+ 2014-01-09 14:47:11 -05:00
f2912bad95 fileUtils: Remove listDirAsync()
It's unused since commit da4238ec68, just kill it.

https://bugzilla.gnome.org/show_bug.cgi?id=721629
2014-01-09 13:23:26 -05:00
c8adfe0131 Updated Hebrew translation 2014-01-08 17:32:29 +02:00
8b7e637e74 Updated Spanish translation 2014-01-08 12:43:32 +01:00
43cffd7c4a main: allow session mode to be specified in the environment
Specifying the session mode on the command-line doesn't play
well with session management (since the saved session desktop
file well either drop the specified session mode, or force it
always, even if the user picked a different mode at the login
screen)

This commit adds support for specifying the session mode via an
enviroment variable. For now, keep the old command line interface
for backward compatibility

https://bugzilla.gnome.org/show_bug.cgi?id=720894
2014-01-07 16:36:26 -05:00
f3dad3765e Changed obsolete FSF postal address.
https://bugzilla.gnome.org/show_bug.cgi?id=721507
2014-01-08 04:35:14 +07:00
70c25141fc Tajik translation updated 2014-01-07 23:32:30 +05:00
b1b81a2672 Improve some strings in gschema file 2014-01-07 18:55:25 +01:00
46197bf262 Tajik translation updated 2014-01-07 13:49:47 +05:00
58ec409e7f [l10n] Updated Italian translation. 2014-01-06 09:42:06 +01:00
c2d68599de Update Kazakh translation 2014-01-05 20:31:42 +06:00
65f00f3af2 ShellWindowTracker: remove gtk-doc marks from private functions
static internal functions should be documented with /*, not /**

https://bugzilla.gnome.org/show_bug.cgi?id=721439
2014-01-04 17:34:29 +01:00
6544326ffd ShellWindowTracker: fix reference counting of ShellApp
All get_app_from_*() helpers are transfer full, but
get_app_from_gapplication_id() was directly returning the result
of lookup_app(), which is transfer none.

https://bugzilla.gnome.org/show_bug.cgi?id=721439
2014-01-04 17:34:10 +01:00
a23c206ccb Updated Indonesian translation 2014-01-04 10:25:11 +07:00
1b152e6bd0 WorkspacesView: fix removal of workspaces that are not at the end
Workspaces can removed from any index, and in particular they
will be removed by the WorkspaceTracker if they stop containing
windows at some point. Make sure WorkspacesView is not confused
and destroyes the right Workspace objects.

https://bugzilla.gnome.org/show_bug.cgi?id=721417
2014-01-03 22:46:45 +01:00
d9624d9882 Updated Lithuanian translation 2014-01-03 23:13:04 +02:00
178b8471cc shell-global: Remove an explicit js-version set
gjs's default js-version is already 1.8, and we're planning on removing
this API in the future, in accordance with upstream.
2014-01-02 13:41:14 -05:00
719d2092a7 Updated German translation 2014-01-02 14:43:18 +01:00
2f3a4675da Updated Brazilian Portuguese translation 2014-01-02 04:16:35 -02:00
9513be664b Updated Slovenian translation 2014-01-01 20:06:54 +01:00
64d8b7853a Update Chinese simplified translation 2014-01-01 17:02:30 +08:00
4174e57c13 Updated Spanish translation 2013-12-30 11:55:56 +01:00
88b395599a Updated Czech translation 2013-12-30 00:10:17 +01:00
b6d682c92c Updated Czech translation 2013-12-29 23:55:23 +01:00
3b02894341 Updated Brazilian Portuguese translation 2013-12-26 23:45:28 -02:00
f3feb13dfe ShellAppSystem: own the memory for the startup wm class and app id
The hash table must keep a copy of the IDs, because the GAppInfos
are unreferenced (and thus freed) at the end of the function.
This was possibly not a problem if the GAppInfos were referencing
the memory-mapped cache, but it becomes one for regularly parsed
desktop files in ~/.local.

https://bugzilla.gnome.org/show_bug.cgi?id=721039
2013-12-26 23:44:54 +01:00
114d8d0aba [l10n] Updated Italian translation. 2013-12-24 10:27:16 +01:00
503a843bb3 Updated Norwegian bokmål translation 2013-12-23 10:25:28 +01:00
c4d91aff40 Updated Aragonese translation 2013-12-22 21:16:13 +01:00
2ffe5faf6e Updated Slovenian translation 2013-12-22 19:24:13 +01:00
5d2a03aa82 Updated German translation 2013-12-22 18:59:27 +01:00
f4c83d1221 Update Chinese simplified translation 2013-12-22 22:54:09 +08:00
100e91714b Updated slovak translation 2013-12-22 13:14:30 +01:00
dafb7b5259 Bump version to 3.11.3
Update NEWS.
2013-12-19 21:54:37 +01:00
92906e217c Updated Spanish translation 2013-12-19 14:36:46 +01:00
5c7b721879 Tajik translation updated 2013-12-19 14:13:33 +05:00
c27dcb0414 Updated Galician translations 2013-12-19 01:53:09 +01:00
7fcae1e974 Remove use of superfluous MetaWindowActor APIs 2013-12-16 12:48:53 -05:00
cc4659f5c6 calendar: Don't rebuild the entire calendar widget when choosing a date
It's inefficient and wasteful. Combined with the JS GC not being great,
it leaks around 100 actors waiting to be GC'd. Yikes.

https://bugzilla.gnome.org/show_bug.cgi?id=720298
2013-12-16 12:44:23 -05:00
9fce12d6b4 calendar: Don't ever force reload
https://bugzilla.gnome.org/show_bug.cgi?id=720298
2013-12-16 12:44:22 -05:00
deb2f30b37 js: Use EVENT_PROPAGATE/EVENT_STOP constants in event handlers
Just as SOURCE_CONTINUE/SOURCE_REMOVE in source functions, these
constants increase code clarity over plain true/false.

https://bugzilla.gnome.org/show_bug.cgi?id=719567
2013-12-16 18:27:19 +01:00
751a3f0e94 js: Use SOURCE_CONTINUE/SOURCE_REMOVE constants in source functions
With support for boolean constants in g-i, we can finally use the
more readable constants instead of true/false.

https://bugzilla.gnome.org/show_bug.cgi?id=719567
2013-12-16 18:27:19 +01:00
fee2a07e08 runDialog: Explicitly set horizontal alignment
When set to fill, the label will always end up left-aligned, which
is only correct in LTR locales. Set the alignment explicitly to
work in both RTL and LTR locales.

https://bugzilla.gnome.org/show_bug.cgi?id=712579
2013-12-16 18:27:19 +01:00
17726abb0a NetworkAgent: handle empty hints and VPN secrets correctly
get_secrets_keyring_cb() contained an optimization (copied over from
nm-applet) that avoided a D-Bus round-trip when NetworkManager sent
secrets hints that were not satisified by the user.  This code did
not properly handle empty hints though, and proceeded to always
request new secrets whenever empty hints were sent.  Remove this
code entirely since the complexity is not worth it (per Jasper).

Second, get_secrets_keyring_cb() was mishandling VPN secrets which
were marked as "always ask".  Because the VPN secrets are not GObject
properties because they cannot be pre-defined, they are passed in
a hash table that is a GObject property marked 'secret'.  Unfortunately,
that means that the shell agent cannot determine their secret flags.
But since the VPN plugin auth dialogs have much better information
about what's required than the shell agent does, always ask the VPN
auth dialogs to handle the secrets requests after grabbing any that
already exist from the keyring.  This is also what nm-applet does.

https://bugzilla.gnome.org/show_bug.cgi?id=719815
2013-12-13 16:23:24 -06:00
01f740ce69 background: Don't prematurely remove file monitors
We need to only remove file monitors when there's no other users
of the content...
2013-12-13 11:50:17 -05:00
b168ccb605 st-scroll-view: Fix style 2013-12-11 20:36:44 -05:00
04a31a52ae calendar: Fix style 2013-12-11 20:36:44 -05:00
6a236fb91e keyring: Align more a srtings to the right side in RTL
It is missed in the previous patch.

Signed-off-by: Yosef Or Boczko <yoseforb@gmail.com>
2013-12-11 23:23:23 +02:00
619fa1bff8 polkitAgent: Align more a string to the right side in RTL
Signed-off-by: Yosef Or Boczko <yoseforb@gmail.com>
2013-12-11 23:14:08 +02:00
53b37e8d0c endSessionDialog: Align some strings to the right in RTL
https://bugzilla.gnome.org/show_bug.cgi?id=712600
2013-12-11 22:42:01 +02:00
f8eb8adfbe keyring: Align some srtings to right in RTL
https://bugzilla.gnome.org/show_bug.cgi?id=712594
2013-12-11 22:40:18 +02:00
3c2aecb81f polkitAgent: Explicitly set horizontal alignment
When set to fill, the label will always end up left-aligned, which
is only correct in LTR locales. Set the alignment explicitly to
work in both RTL and LTR locales.

https://bugzilla.gnome.org/show_bug.cgi?id=712596
2013-12-11 22:38:58 +02:00
efca9e11d6 unlockDialog: Explicitly set horizontal alignment
When set to fill, the label will always end up left-aligned, which
is only correct in LTR locales. Set the alignment explicitly to
work in both RTL and LTR locales.

https://bugzilla.gnome.org/show_bug.cgi?id=712638
2013-12-11 22:36:49 +02:00
49189e0e43 authPrompt: Explicitly set horizontal alignment
When set to fill, the label will always end up left-aligned, which
is only correct in LTR locales. Set the alignment explicitly to
work in both RTL and LTR locales.

https://bugzilla.gnome.org/show_bug.cgi?id=712638
2013-12-11 22:36:15 +02:00
ea86c9bafb userWidget: Fix the padding in RTL
https://bugzilla.gnome.org/show_bug.cgi?id=712638
2013-12-11 22:35:44 +02:00
c361b6a85c Update Arabic translation 2013-12-11 06:53:36 +02:00
c7ff45045c remoteSearch: Let remote search providers not provide an icon
The documentation indicates that they are optional, so let us make the
code behave accordingly.

https://bugzilla.gnome.org/show_bug.cgi?id=719965
2013-12-10 17:01:42 +01:00
090af35ea1 Finnish translation update 2013-12-09 08:08:10 +02:00
8e668ca633 sessionMode: Add back external session modes
Since commit da4238ec68, external session modes are no longer
loaded during start-up; bring them back.

https://bugzilla.gnome.org/show_bug.cgi?id=720017
2013-12-07 10:10:06 +01:00
4d9a16f33b sessionMode: Fix listModes()
Commit da4238ec68 just broke that function completely by
calling array methods on objects and other funny stuff. Fix it.

https://bugzilla.gnome.org/show_bug.cgi?id=720017
2013-12-07 10:10:06 +01:00
c9f2a0f694 sessionMode: Rename _getModes()
Since commit da4238ec68, the function doesn't return anything,
so rename it to _loadModes() to reflect this.

https://bugzilla.gnome.org/show_bug.cgi?id=720017
2013-12-07 10:10:06 +01:00
46bac3069e sessionMode: Fix left-over variable in _loadMode()
Commit da4238ec68 dropped a variable, but didn't replace all
consumers.

https://bugzilla.gnome.org/show_bug.cgi?id=720017
2013-12-07 10:10:06 +01:00
d21aa0d85f shell-app: Track all application windows
Filtering out "non-interesting" windows beforehand as we currently do
means that we may get properties that should be based on all windows,
like the last time the application was used, wrong.
Just track all windows and filter out non-interesting windows manually
in the one place we actually care about the difference.

https://bugzilla.gnome.org/show_bug.cgi?id=719824
2013-12-07 10:10:06 +01:00
3e87d699eb [l10n]Updated Turkish translation 2013-12-07 08:20:27 +02:00
23aa216908 [l10n] Updated Estonian translation 2013-12-05 12:45:57 +02:00
e34e681157 messageTray: Fix style 2013-12-05 01:32:28 -05:00
acd543fe4b messageTray: Fix style
We're missing a semi here
2013-12-04 20:51:10 -05:00
c2df5939d6 Sort js-resources.gresource.xml 2013-12-04 20:44:22 -05:00
b52e74b615 messageTray: Remove transient sources
As far as I can tell, the only behavior change of a transient source
is that they auto-destroy after viewing their summary box pointer.
Since all transient sources are only associated with transient
notifications, it seems that we can never get to their summary box
pointer in the first place! Remove support for this.

https://bugzilla.gnome.org/show_bug.cgi?id=710115
2013-12-04 20:25:28 -05:00
ec2bb039ae viewSelector: Give the active page key focus when it is shown
Rather than implement special focus policies like only allowing keynav
when pressing down, simply give the active page key focus when entering
the overview.

This may break stuff, as it's somewhat of a tricky patch to get right.
Testing this one would be super appreciated.

https://bugzilla.gnome.org/show_bug.cgi?id=644306
2013-12-04 11:22:55 -05:00
aeb9f5775f workspace: Grab the key focus when hovering over a window
This is simply an experiment. I'm not sure I like the result, but it was
worth trying out regardless.

https://bugzilla.gnome.org/show_bug.cgi?id=644306
2013-12-04 11:22:55 -05:00
47a20756b9 workspace: Implement key navigation on the workspaces page
Simply use St's existing key navigation system by making all the window
clones StWidgets, and making the WorkspacesView a focus group.

Since the workspace view is effectively "fake", we need to add a focus
delegator so that when key focus is assigned to the fake workspaces page,
we can keynav inside it properly.

https://bugzilla.gnome.org/show_bug.cgi?id=644306
2013-12-04 11:22:55 -05:00
a7aba1d585 st-widget: Sort actors by distance from the focused actor
Sorting actors by the distance in the axis of movement first and against
the axis otherwise means that if we have a situation like:

  A      F
   B

where "F" is the focused actor, and it slightly overlaps with B vertically,
then we'll choose "B" to go left, rather than "A", which is most likely
what the user intended.

This is especially apparent in the overview where slight window size
differences mean we might not get an exact grid shape.

https://bugzilla.gnome.org/show_bug.cgi?id=644306
2013-12-04 11:22:55 -05:00
c89af0cea4 messageTray: Only attempt to grab the summary box pointer after showing it
For mysterious reasons I'm not sure of myself, navigate_focus will only focus
mapped actors. So, make sure the widget is showing before navigating to it.

https://bugzilla.gnome.org/show_bug.cgi?id=709853
2013-12-04 11:22:55 -05:00
9d3a109946 messageTray: Reword a boolean condition
Just so it's a bit easier to understand...
2013-12-04 11:22:55 -05:00
079f1e6fff messageTray: Fix style 2013-12-04 11:22:55 -05:00
7249b11899 background: Don't silently fizzle out when removing bad content from the cache
If the background is already removed, or we're trying to remove bad content,
this is probably a bug in content accounting, so let us crash so we can fix
the bugs.

https://bugzilla.gnome.org/show_bug.cgi?id=719803
2013-12-03 19:13:12 -05:00
4cfb000812 background: Add copied content from pending image loads to the cache
https://bugzilla.gnome.org/show_bug.cgi?id=719803
2013-12-03 19:13:12 -05:00
5262a41619 background: Clarify the intent of the code
Stomping on local variables and trying to keep loop state isn't
too fun. Just use a new variable here so we aren't too confused
with what we're doing.

https://bugzilla.gnome.org/show_bug.cgi?id=719803
2013-12-03 19:13:12 -05:00
887590730d background: Simplify animation code
https://bugzilla.gnome.org/show_bug.cgi?id=719803
2013-12-03 19:13:12 -05:00
adb49bdf0b background: Remove unused variable
https://bugzilla.gnome.org/show_bug.cgi?id=719803
2013-12-03 19:13:12 -05:00
7f3aadc157 background: Remove the system noise content when not in use
https://bugzilla.gnome.org/show_bug.cgi?id=719803
2013-12-03 19:13:12 -05:00
eb1c85f3f5 background: Don't wait for gdk-pixbuf to fail before loading animations
We don't have any better way of determining whether something is a slideshow
animation, so discriminate on the .xml filename instead of waiting for
gdk-pixbuf to determine whether it can load a file or not.

https://bugzilla.gnome.org/show_bug.cgi?id=719803
2013-12-03 19:13:12 -05:00
dbdc884c96 background: Remove more bogus checks
The content in these arrays can never be null...

https://bugzilla.gnome.org/show_bug.cgi?id=719803
2013-12-03 19:13:12 -05:00
5166354e34 networkAgent: add support for EAP-FAST password requests
EAP-FAST is a top-level EAP method like TTLS, PEAP, etc.

https://bugzilla.gnome.org/show_bug.cgi?id=719813
2013-12-03 18:03:52 -06:00
04ea95049a background: Fix the check for spanning backgrounds
this._monitorIndex does not exist, and neither does
MetaBackground.monitor_index...

https://bugzilla.gnome.org/show_bug.cgi?id=719803
2013-12-03 18:36:46 -05:00
ec62e49001 bluetooth: Remove unused import 2013-12-03 18:36:46 -05:00
6fb21850d8 Updated Brazilian Portuguese translation 2013-12-02 23:17:13 -02:00
98b50fd942 UserWidget: replace vfunc_destroy override with a signal connection
The destroy vfunc might be called during object finalization, and
we can't call any JS from a GC finalizer, so we use a signal
connection instead, as that is removed by GObject the first time
the object is disposed.

https://bugzilla.gnome.org/show_bug.cgi?id=719730
2013-12-02 23:59:50 +01:00
729c962b7c loginDialog: Implement cancel()
The screen shield expects a cancel() method on the unlockDialog
implementation, but LoginDialog does not provide it currently.

https://bugzilla.gnome.org/show_bug.cgi?id=719378
2013-11-27 14:30:24 +01:00
ebc15e60a8 bluetooth: Remove GnomeBluetoothApplet hacks
https://bugzilla.gnome.org/show_bug.cgi?id=719341
2013-11-26 18:53:18 +01:00
85f2d94253 bluetooth: Use BluetoothClient to detect connected devices
Instead of the GnomeBluetoothApplet helper.

https://bugzilla.gnome.org/show_bug.cgi?id=719341
2013-11-26 18:53:18 +01:00
981a536cb5 bluetooth: Use g-s-d to turn off Bluetooth
https://bugzilla.gnome.org/show_bug.cgi?id=719341
2013-11-26 18:53:18 +01:00
abf7c333b1 bluetooth: Remove pairing agent
We'll only have it in the Bluetooth settings panel.

https://bugzilla.gnome.org/show_bug.cgi?id=719341
2013-11-26 18:53:18 +01:00
b2f547e934 authPrompt: propagate gdm "reset" signal after user switching
After a user types in their password at the login screen, one
of two things can happen

1) a new session is started
2) an existing session is switched to

In the latter case, GDM sends a reset signal to the login screen,
so it knows to go back to the user list and wait to be summoned
again.

Unfortunately, all reset signals are ignored after verification
success.  The reason is because the reset handler was copied from
the unlock dialog as part of a deduplication effort in commit
7e7295f259 and the unlock dialog
handler at the time also emitted a "failed" signal on reset
(which wouldn't make sense to emit after success).

These days "failed" is handled in a different way.

This commit changes the code to let reset signals through after
successful verification.

https://bugzilla.gnome.org/show_bug.cgi?id=710456
2013-11-25 22:38:44 -05:00
0870a25e2f Typo fixed in Dutch translation 2013-11-22 22:44:13 +01:00
1091e577a5 Updated Dutch translation by Erwin Poeze 2013-11-22 22:41:18 +01:00
151ad16fe6 Updated Norwegian bokmål translation 2013-11-21 21:24:50 +01:00
e325258091 build: require gjs 1.39.0
This is required for the gresources loader
2013-11-22 06:58:33 +11:00
1139a02b40 viewSelector: Don't show pages until they need to be visible
AppDisplay queues a deferred work to load frequently used apps when the
apps page is loaded. Unfortunately, when the overview is first opened,
all the pages start out visible and then immediately get hidden, so the
deferred work runs immediately after the first overview opening, whether
the user was going to view their frequent apps or not.

Start all pages off as hidden, and rearrange the code so that pages are
only shown when they really need to be.

https://bugzilla.gnome.org/show_bug.cgi?id=712753
2013-11-21 12:50:03 -05:00
4b90953226 osdWindow: add setMonitor() to allow changing the monitor
This is also exposed in the ShowOSD DBus method, the "monitor"
parameter may contain an integer to indicate the monitor number.
If the value is not provided or <0 is used, the monitor is shown
on the primary monitor as usually.

This way, the OSD can be used to notify upon events that solely
apply to one monitor, like tablet mapping as discussed in
https://bugzilla.gnome.org/show_bug.cgi?id=710373.

https://bugzilla.gnome.org/show_bug.cgi?id=712664
2013-11-20 18:03:32 +01:00
d77fc01580 boxpointer: Don't hide when we're already hidden
You would think we would already do something like this, but apparently
lots of code was calling hide() without checking if the box pointer was
already visible, causing it to queue a full tween. The biggest win was
with ibusCandidatePopup.js, which called hide() on every DBus message.

This increases the performance for me to enter the overview by a tiny
bit. The remaining time is spent updating the frequent apps / all apps
display.

https://bugzilla.gnome.org/show_bug.cgi?id=712727
2013-11-19 23:23:25 -05:00
216d84faeb layout: Adjust the opening animation to be less intense
https://bugzilla.gnome.org/show_bug.cgi?id=712362
2013-11-19 22:09:34 -05:00
0c9d95f183 userWidget: Chain up in destroy() 2013-11-19 18:11:43 -05:00
913739d732 shell-app: Remove unused method 2013-11-19 18:11:43 -05:00
7ecb5af587 appDisplay: Remove unused signal
The signal was last used in the pre-3.0 days, so we can stop dragging
it along and just remove it.
2013-11-18 16:24:13 +01:00
87f0e79749 messageTray: Prevent reentrancy issues in _updateState
The methods we call in _updateState may not be reentrant, so make
sure that we never get into a situation where _updateState, through
some crazy chain of events, calls itself.

https://bugzilla.gnome.org/show_bug.cgi?id=711694
2013-11-17 12:06:38 -05:00
c85145d73c entry: Make sure we chain up in enter/leave handlers
To ensure that the focus tracking executes correctly.

https://bugzilla.gnome.org/show_bug.cgi?id=706749
2013-11-15 12:51:19 -05:00
eea689841b highlight session menu button on key focus
https://bugzilla.gnome.org/show_bug.cgi?id=710539
2013-11-15 11:29:21 -05:00
e50a59361d entry: Remove old documentation about the hover style class 2013-11-15 11:14:36 -05:00
9862185bda theme: Clean up
Remove some unused CSS
2013-11-15 11:14:35 -05:00
fe05d35bbb messageTray: Fix style 2013-11-15 10:39:30 -05:00
ba602c17d4 appDisplay: Use the desktop file index for app searching
Rather than scanning all apps for searching, use Ryan's new desktop
file index and the glib support APIs for app searching instead of our
own system.

https://bugzilla.gnome.org/show_bug.cgi?id=711631
2013-11-14 14:28:52 -05:00
831bd07b0d layout: Fix several issues with the background management code
If monitor-changed fires at startup, it will destroy all of the
backgrounds, but since this._isStartup is true, won't recreate any
of them. Additionally, since _bgManagers is indexed by monitor index,
if the primary index is not 0, it could become a sparse array (e.g.
[undefined, undefined, primaryBackground]), and our for loop will
crash trying to access properties of undefined.

Fix both of these issues by always creating background managers for
every monitor, hiding them on startup but only showing them after
the startup animation is complete.

One thing we need to watch out for is that while LayoutManager is
constructing, Main.uiGroup / Main.layoutManager will be undefined,
so addBackgroundMenu will fail. Fix this by passing down the uiGroup
to the background menu code.

https://bugzilla.gnome.org/show_bug.cgi?id=709313
2013-11-14 14:28:51 -05:00
175c5d9fc3 overview: Fix stacking of background and desktop icons
If desktop icons are enabled and not covered by maximized windows,
we will fade them in/out during overview transitions. However when
moving background handling into mutter/gnome-shell, we ended up with
the overview background on top of the DESKTOP window clone, hiding
the fade transition.
Fix the stack order to bring the effect back.

https://bugzilla.gnome.org/show_bug.cgi?id=707671
2013-11-14 15:49:28 +00:00
2639e30d9c Bump version to 3.11.2
Update NEWS.
2013-11-13 21:24:30 +01:00
78a0218a91 build: Add built js-resources to CLEANFILES 2013-11-13 21:24:30 +01:00
e12bf8daed shellDBus: Fix error message returned from Eval
Annoyingly, `message` on errors are non-enumerable, which mean that
JSON.stringify on the error will produce `{}`. Just cast it to a string
for now.
2013-11-13 15:15:03 -05:00
04e2072e2c Updated Tamil Translations 2013-11-11 14:45:06 +05:30
7bafe20a34 Update Chinese simplified translation 2013-11-10 14:19:51 +08:00
554d5aeb7c More invalid source fixes
https://bugzilla.gnome.org/show_bug.cgi?id=711732
2013-11-09 17:58:59 +01:00
3991d2729d dash: Make sure we clear the timeout IDs for the label tooltip
https://bugzilla.gnome.org/show_bug.cgi?id=711732
2013-11-09 11:44:44 -05:00
5bc8a0860a tweener: Remove the onAnimationStart/onAnimationComplete callbacks
Our gnome-shell tweener integration has had hooks to determine when
the tweens have started and completed... except that they had a bug
in them. When a tween completed, it queued an idle handler to run
the callback in. If no tweens were running when the idle was removing,
it reset the tween state that contained the idle handler ID. It also
returned false, meaning that the source would always get removed.

If the actor had a tween in-flight when the idle was fired, it wouldn't
clean up after itself. While this is also a simple bug fix, remove the
callback so we don't queue unnecessary, unused idles.

https://bugzilla.gnome.org/show_bug.cgi?id=711732
2013-11-09 11:44:44 -05:00
ad03fb0815 app-system: Add back StartupWMClass matching
While unfortunate that we still have to scan all apps with get_all(),
support for this feature will be short-lived, so hopefully we can drop
it in the future as new apps adapt to the desktop file / app ID
recommendations.

For now, simply scan all desktop IDs.

https://bugzilla.gnome.org/show_bug.cgi?id=711631
2013-11-07 16:35:03 -05:00
e10d2a68f3 app-system: Put back support for the installed-changed signal
Use the new GAppInfoMonitor that Ryan added to glib to know when the
set of apps has changed.

https://bugzilla.gnome.org/show_bug.cgi?id=711631
2013-11-07 16:35:03 -05:00
213ee8d381 ShellApp: Connect applications to systemd journal (if available)
Systemd-for-the-user-session would also do this, but that's a deeply
invasive change that I may not actually get to this cycle.  This
change is tiny and non-invasive, but provides an important benefit:
You can actually reliably tell *which* applications are logging which
messages (assuming they're launched by the shell).

This actually complements a recent change in DBus:
See https://bugs.freedesktop.org/show_bug.cgi?id=68559
which does a similar thing for bus activated apps.

https://bugzilla.gnome.org/show_bug.cgi?id=711626
2013-11-07 13:44:03 -05:00
52b1a1b835 popupMenu: Fix removing the active menu from PopupMenuManager
Commit b42af9aa99 changed the parameter list of _closeMenu()
to account for changes in the GrabHelper ungrab mechanism, but
didn't update other callers.

https://bugzilla.gnome.org/show_bug.cgi?id=709806
2013-11-07 00:09:50 +01:00
fce2930b85 dnd: Don't queue an idle handler if we already have one
Removing an existing source before scheduling a new one is not wrong,
but slightly less effective than doing nothing and relying on the
previously created source to do the job.

https://bugzilla.gnome.org/show_bug.cgi?id=711555
2013-11-06 18:36:42 +01:00
735f589b1c dnd: Don't try to remove an invalid idle source
As the handler returns false, the corresponding source is removed
automatically and its id invalidated. Reset the id to 0 to reflect
this, otherwise newer versions of GLib will print a warning when
we later try to remove it explicitly.

https://bugzilla.gnome.org/show_bug.cgi?id=711555
2013-11-06 16:40:50 +01:00
69f17da5ca trivial: Fix the signedness of boolean fields
The standard old kludge with gboolean being signed, not unsigned.
Encountered while printing the values for debugging.

https://bugzilla.gnome.org/show_bug.cgi?id=644306
2013-11-05 09:11:49 -05:00
5c0ee02251 trivial: st-widget: Remove super old 'stylable' property
I don't think we ever used this, even way back in 3.0...

https://bugzilla.gnome.org/show_bug.cgi?id=644306
2013-11-04 21:11:00 -05:00
0cb4c7e437 Updated German translation 2013-11-04 21:05:26 +01:00
842c792868 search: Only do a subsearch if the previous results have returned from DBus
There's a potential race condition in the search code: if we have an
outstanding search call to a provider for search "A", and if before it comes
back we do a subsearch for "AB", we won't have any results to pass along.

Previously, we used an empty list when storing the provider results, so we
effectively told the remote search app to filter through this empty list for
any search results that meet the new query, meaning we showed the user 0
results for the provider in this case.

Now that we don't store an empty list, but instead store `undefined`, this race
raises a warning. Solve it by doing an initial search query in this case
instead.

The search code isn't too smart about chained subsearches: now, if we hit this
race while already on a subsearch, we'll do an initial search for the subsearch
query instead, but that is much better than showing the user nothing. This
could be fixed in the future for a performance improvement.

Reviewed-by: Florian Müllner <fmuellner@gnome.org>
2013-11-04 14:50:45 -05:00
4ba8518462 messageTray: Use a regular tween when expanding the notification
When a notification becomes expanded, it's either already shown,
or in the process of being shown. Don't set the state to SHOWING
again, which confuses our state machine.
2013-11-04 14:25:29 -05:00
143dfb6246 messageTray: Simplify code
If notificationRemoved, then mustClose is true, so we don't need to
double-check for this.
2013-11-04 14:16:28 -05:00
da4238ec68 Synchronize shell startup
The asynchronous nature of extension loading, session loading, and more,
makes the code racy as to what is initialized first, and hard to debug.
Additionally, since gjs is single-threaded, the only code we're running
in a thread anyway is readdir, which is going to be I/O bound, so the
code here is actually likely to be faster.

Drop this in favor of some good old fashioned synchronous loading.
2013-11-04 11:50:20 -05:00
5f9e3edbe1 notificationDaemon: Only store policies for "real" apps
Fake, window-backed apps should not have a policy for them.
2013-11-04 11:47:43 -05:00
1c68aee577 screenShield: Fix details in notifications
bannerBodyMarkup is a boolean flag to indicate that bannerBodyText
contains markup, not the markup text itself.

https://bugzilla.gnome.org/show_bug.cgi?id=711416
2013-11-04 16:46:25 +01:00
e8d9a4bd49 screencast: Validate parameters of ScreencastArea
... just as we do for screenshots.

https://bugzilla.gnome.org/show_bug.cgi?id=699752
2013-11-04 16:21:45 +01:00
9520e87a38 screencast: Fix disabling screencasts via session mode
If screencasts are disabled, we return a DBus error, but still start
the recording happily - add early returns in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=699752
2013-11-04 16:21:45 +01:00
f9f5004909 screenshot: Extend ScreenshotArea parameter validation
We currently only ensure that width and height are positive, so it
is still possible to pass in values that don't make any sense at all
(which may even result in a crash when exceeding limits imposed by
X11).
There is nothing to screenshot outside the actual screen area, so
restrict the parameters to that.

https://bugzilla.gnome.org/show_bug.cgi?id=699752
2013-11-04 16:21:45 +01:00
4f7014b2d5 networkAgent: Make sure to update the OK button on dialog pop up
For consistency.
2013-11-04 08:55:59 -05:00
01dbfddb64 workspace: Remove unused includes and constants 2013-11-04 10:32:17 +01:00
6266a22d86 Updated Greek translation 2013-11-04 10:53:08 +02:00
09fe31179a app-system: Fix memory leak
The shell app takes a ref on the info.
2013-11-03 16:20:00 -05:00
78343f4837 app-system: Fix bad code
I had this locally, but forgot to amend before pushing apparently
2013-11-03 16:19:53 -05:00
a5619bc0a3 app-system: Fix const warning 2013-11-03 12:50:21 -05:00
dcb28aad2a app-system: Fix the keys in the id_to_app table
The ID that's passed to us isn't usable as a key to store, as it's
probably junk after the method returns. Use the app's storage for
the ID instead.
2013-11-03 12:10:00 -05:00
754cf172f5 Updated Norwegian bokmål translation 2013-11-03 16:03:44 +01:00
7890af1659 app-system: Lazily create ShellApps for apps we care about
Rather than create all ShellApps up-front, create them lazily. We really
had no reason to do this before as we were scanning GMenu to get all the
apps, but doing this can remove a need for get_all, which is slow and
memory-hungry.
2013-11-02 20:50:35 -04:00
d27d9fe694 app-system: Remove use of gnome-menus internally
We want to transition to a system in the future where we have a desktop
file cache. As we no longer differentiate categories or similar, it no
longer makes sense to have app visibility based on categories. Thus,
we no longer need to use gnome-menus to list all apps. The potential
issue here is reloading all desktop files when new files are created,
but this can be dealt with individually.

The "All Applications" view still uses gnome-menus.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-11-02 20:12:37 -04:00
634a599db6 appDisplay: Ignore the NoDisplay flag for directories
This makes us match the native app search.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-11-02 20:12:37 -04:00
5d0d859a1f app-system: Remove visible_id_to_app
Since appDisplay.js makes its own GMenu tree, it's not necessary
anymore. This does mean that searches will show apps in NoDisplay
categories, but that's an obscure enough edge case not to matter.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-11-02 20:12:37 -04:00
40c966fcd6 app-system: Remove lookup_app_for_path
It's absurdly silly. Just modify the one place that uses it
to be better.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-11-02 20:12:37 -04:00
d9245598a4 app-system: Remove known_vendor_prefixes
This does remove support for legacy prefixed app infos with
subdirs, but since we want to remove support for the menu spec,
let's not even bother.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-11-02 20:12:37 -04:00
03b0f4b16b app-system: Remove get_tree
Make clients construct their own gmenu tree if they need it.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-11-02 20:12:37 -04:00
2daa0d057b app-system: Map wmclass to ID rather than apps
This makes the refcounting and memory management easier to understand.
2013-11-02 20:12:36 -04:00
76eca409a3 app-system: Don't use gmenu_tree_entry_get_desktop_app_info
It's a broken method when it comes to giving us a useful GDesktopAppInfo,
and it's hard to fix libgmenu properly, so simply recreate the app info
using the desktop file ID that libgmenu has.
2013-11-02 20:12:36 -04:00
d84b018ba7 app: Port to be based on GDesktopAppInfo
We weren't using the GMenuTreeEntry for anything special anymore,
so remove it.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-11-02 20:12:36 -04:00
027c3d1661 app-system: Remove lookup_app_by_tree_entry
We want to move away from gnome-menus eventually, so the simple
utility method isn't really worth keeping around. Reimplement it
in the one place that uses it.

https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-11-02 20:12:36 -04:00
4965b1ca7b search: Fix previous commit
It was pushed by accident before it was tested...
2013-11-02 20:11:13 -04:00
9cd7ea9371 search: Make the internal search interface callback-based
Long ago, the search system worked in a synchronous manner: providers
were given a query, and results were collected in a single array of
[provider, results] pairs, and then the search display was updated
from that.

We introduced an asynchronous search system when we wanted to potentially
add a Zeitgeist search provider to the Shell in 3.2. For a while, search
providers were either async or sync, which worked by storing a dummy array
in the results, and adding a method for search providers to add results
later.

Later, we removed the search system entirely and ported the remaining
search providers to simply use the API to modify the empty array, but the
remains of the synchronous search system with its silly array still
lingered.

Finally, it's time to modernize. Promises^WCallbacks are the future.

Port the one remaining in-shell search engine (app search) to the new
callback based system, and simplify the remote search system in the
process.
2013-11-02 20:07:06 -04:00
dc2468b27b remoteSearch: Do filtering here of providers here
We already do ordering at load time, so why not filtering?
2013-11-02 20:07:06 -04:00
ea2451d882 overviewControls: Fix bad expression causing incorrect thumbnails allocation
`a + b ? c : d` is parsed as `(a + b) ? c : d`, not the more intuitive
`a + (b ? c : d)`.

This was causing a bad slide animation and Clutter warnings when coming
out of the overview.
2013-11-02 20:07:06 -04:00
252617bd70 search: Remove unnecessary import and full reference
I'm surprised this worked, actually...
2013-11-02 18:01:26 -04:00
8bd7003ea7 bluetooth: Parse pins starting with 0 correctly
If we have a numeric PIN that starts with 0, it will be treated
as an octal number rather than a number that just starts with 0.
2013-11-02 17:55:54 -04:00
280203158c Updated Russian translation 2013-11-01 23:32:39 +04:00
d456c3f62e system: Restore support for 'disable-restart-buttons'
The org.gnome.login-screen schema contains a key to disable the
power/restart buttons; our support for this fell victim to the
new combined status menu, add it back.

https://bugzilla.gnome.org/show_bug.cgi?id=711244
2013-11-01 13:08:02 +01:00
f64d17963b Support disabling browser plugin
Some downstreams may not want it for security reasons.

https://bugzilla.gnome.org/show_bug.cgi?id=711218
2013-10-31 12:44:51 -04:00
f12378cf7b search: Hide overview when activating result
This was (accidentally?) dropped by commit 3749b09366.

https://bugzilla.gnome.org/show_bug.cgi?id=711205
2013-10-31 16:19:25 +00:00
15ff426be8 shell_global_reexec_self: add support for OpenBSD
https://bugzilla.gnome.org/show_bug.cgi?id=709571
2013-10-31 11:46:59 +01:00
c4a6837d56 build: Make sure built-sources are introspected as needed 2013-10-31 00:01:38 +01:00
e6c28cf509 Revert "overviewControls: Always allocate the actor its full size"
This reverts commit f5a9dbb348.

This broke the alignment of the workspace thumbnail widget. I don't
know why, but let's not break things if we can...
2013-10-30 18:33:13 -04:00
a347a75617 st-theme: Use constructed instead of constructor 2013-10-30 18:33:13 -04:00
7fc2183826 build: Do not ship generated source-files in tarball 2013-10-30 23:29:45 +01:00
b6c3c9891c workspacesView: Make sure to update workspace actors when entering the overview
Otherwise, they will be in the wrong positions.
2013-10-30 18:20:54 -04:00
d401b493a4 search: Fix Return for searching in the overview 2013-10-30 17:43:12 -04:00
da1a8308b6 build: Also look for generated js-resources.* files in builddir 2013-10-30 21:41:52 +01:00
106d827a21 build: Also look for resources in $(builddir)
Since misc/config.js is built, we need to tell the resource compiler
to find it too.

Fixes the build in Continuous where srcdir != builddir.
2013-10-30 14:00:58 -04:00
1ebb162a00 Load JS from GResource
Since gjs can now load JS from a GResource, compile it in the
gnome-shell binary. This should be a bit faster, and make life easier
with JHBuild.
2013-10-30 13:27:16 -04:00
9d2791d9f8 dnd: Don't use reparent for adding actors to the uiGroup
It's deprecated, terrible, and causes get_theme_node crashes.
2013-10-30 13:20:02 -04:00
04a00f6564 loginDialog: Use UserWidget
https://bugzilla.gnome.org/show_bug.cgi?id=706851
2013-10-30 13:19:02 -04:00
a5dd44c77f userWidget: Use the user name if the real name doesn't fit
This meets the new designs.

https://bugzilla.gnome.org/show_bug.cgi?id=706851
2013-10-30 13:19:02 -04:00
8f86fd6bae workspacesView: Simplify the workspacesOnlyOnPrimary implementation
Before, workspacesOnlyOnPrimary was implemented in quite a crazy manner:

 * If workspacesOnlyOnPrimary was false, we'd create one WorkspacesView per
   monitor, with the primary one being a bit special.

 * If workspacesOnlyOnPrimary was true, we'd create one WorkspacesView, and
   additional montiors would be handled inside that WorkspacesView as
   "extra workspaces".

This caused numerous bugs as the two modes weren't consistently
implemented, and a lot of code was duplicated between all the modes.
Fix this by always creating WorkspaceViews, even if it only handles
one interface. We do this by having two different WorkspacesView-ish
classes: WorkspacesView handles the traditional combination of lots
of workspces, and a new ExtraWorkspaceView is in control of only one
workspace.
2013-10-30 13:17:39 -04:00
d5cd534320 workspacesView: Make each WorkspacesView own its set of workspaces
Right now, the workspace update code is complex and spread across parts:
WorkspacesView takes a set of workspaces and looks like it owns them, but
WorkspacesDisplay is actually in charge of setting them up and creating
new ones for each WorkspacesView.

Change initialization and handling to move all of the creation/destruction
responsibilities to WorkspacesView.

We pass in monitorIndex into each WorkspacesView, which is a lie in the
workspacesOnlyOnPrimary case, as the primary WorkspacesView currently has
the responsibility of handling the extra workspaces on all the other
monitors. The commit will clean this up and punt the responsibility back
to WorkspacesDisplay.
2013-10-30 13:17:39 -04:00
a5a6fd3bc2 workspacesView: Don't tween the upper of our scroll adjustment
This really doesn't make sense. The new upper should be the new workspace
index as soon as it's available.
2013-10-30 13:17:39 -04:00
287ddda5df workspacesView: Don't zoom into the overview based on a signal
Instead, simply have somebody else call us, like we do for hiding
the overview as well.
2013-10-30 13:17:39 -04:00
7747f1c31d workspacesView: Don't use a drag monitor to get the clone
Just use the new variable passed to the signal.
2013-10-30 13:17:39 -04:00
8097cbbbe3 workspacesView: Don't do any special handling for item-dragging
The code here really only cares about new windows.
2013-10-30 13:17:39 -04:00
e4c07875a3 overview: Send the clone with the window-drag events
This allows clients that care about the actual item we're dragging to
make smarter decisions without adding a drag monitor themselves.
2013-10-30 13:17:38 -04:00
026fd4cf35 workspace: Punt the geometry-fizzling-out logic here 2013-10-30 13:17:38 -04:00
87016f9620 workspacesView: Remove spacing
It's not used in the theme and it complicates the layout code a bit
too much; in fact, if set, things start breaking.
2013-10-30 13:17:38 -04:00
88393f0f65 overviewControls: Move translation-x to SlideLayout
Not because ClutterActor is bad or wrong, but because I always get
confused on the difference, and having them both in SlideLayout
makes the code a bit easier to read and understand.
2013-10-30 13:16:45 -04:00
f5a9dbb348 overviewControls: Always allocate the actor its full size
This is the new Clutter way -- if the actor wants to adjust its
alignment, it will do it itself with its alignment flags.
2013-10-30 13:16:45 -04:00
dbf3bb112c overviewControls: Make getSlide/updateSlide private 2013-10-30 13:16:45 -04:00
f3186bd501 overviewControls: Make visible and inDrag private
These don't need to be accessed from outside SlidingControl,
so don't let them be.
2013-10-30 13:16:44 -04:00
3f1a252b91 overviewControls: Move slideOut on overview hide to the parent as well 2013-10-30 13:16:44 -04:00
1240d6be76 overviewControls: Remove dead code
The parent SlidingControl had an onOverviewShowing, but we had
overridden it with the same code in both subclasses. Just move it
back to SlidingControl.
2013-10-30 13:16:44 -04:00
faf7b62f5c overviewControls: Mark the DashSlider as x_expand
Actors need to expand in order for them to their x_aligns to be taken
into account.
2013-10-30 13:16:44 -04:00
445011b1e5 overviewControls: Add the parent's box into the allocation
Currently, this is always 0, but this will change when we introduce
a custom layout manager into the story.
2013-10-30 13:16:44 -04:00
e630fec63a search: Fix adding items to icon grids 2013-10-30 13:06:56 -04:00
17421e8a63 iconGrid: Actually throw programmer errors
This way we get a backtrace.
2013-10-30 13:05:20 -04:00
af06b78605 searchDisplay: Cache result display actors
When we create a result actor, cache it, so it can be used for
subsearches of the same initial. For now, to keep memory usage
and the stage graph relatively clean, don't persist the actors
across searches, but maybe we should do this in the future.

This also means that we don't query getResultMetas for items
that we've seen in the same initial search.

https://bugzilla.gnome.org/show_bug.cgi?id=704912
2013-10-30 13:01:20 -04:00
3749b09366 searchDisplay: Make the search result actors stateless, by removing terms
We want to cache result actors between searches, so we shouldn't
instantiate them with search-specific info.

https://bugzilla.gnome.org/show_bug.cgi?id=704912
2013-10-30 13:01:20 -04:00
27cac10d0c appDisplay: Use a proper string key for the app search provider
Since we're going to be caching results based on the result ID,
we need to return a string-based result ID to cache on.

https://bugzilla.gnome.org/show_bug.cgi?id=704912
2013-10-30 13:00:47 -04:00
0590962d36 viewSelector: Move all of the provider-loading logic to SearchSystem
The existing provider system is split between a confusing mess of
RemoteSearch, SearchSystem, SearchDisplay, and ViewSelector, partly
because of the vestigal in-shell search system. Move most of the
logic to search.js so it's easier to read.
2013-10-30 12:59:41 -04:00
c0c20d49a5 search: Always fetch the list of search providers
We fetch and store the list of providers from the search system when we
construct SearchResults, but we never update this list when providers are
changed at runtime, causing various bugs making the search not seem as
snappy as it should be. Make sure to always fetch the list of providers
from the search system.
2013-10-30 12:59:31 -04:00
cf7cf45003 Bump version to 3.11.1
Update NEWS.
2013-10-30 17:04:40 +01:00
633dd0d9de notificationDaemon: Save notifications on source destruction
While the existing comment is correct in that a source's notifications
will be destroyed first, the code takes a shortcut which prevents the
Source::count-updated signal from being emitted. Given that the purpose
of the signal is to keep notification counters up-to-date which is
pointless when the source is about to be destroyed, the shortcut makes
sense; just save notifications explicitly in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=710596
2013-10-30 16:40:33 +01:00
b929320d4c lookingGlass: Remove Memory tab and add a gc Icon to toolbar
With js24 it won't be possible to access gjs memory stats from C code.

https://bugzilla.gnome.org/show_bug.cgi?id=711052
2013-10-30 07:44:34 +11:00
7296bedd8e remove direct access to jsapi
with js24 its not possible to access jsapi directly from C code

https://bugzilla.gnome.org/show_bug.cgi?id=711052
2013-10-30 07:44:34 +11:00
e9fbbf4000 port shell-js to c++
https://bugzilla.gnome.org/show_bug.cgi?id=711052
2013-10-30 07:44:34 +11:00
dd44219aa5 Merge searchDisplay.js and search.js
search.js used to do a lot more, but now that most of the
functionality has been moved to the remote search system,
it doesn't do a lot. Merge searchDisplay.js into it.
2013-10-29 16:02:32 -04:00
8cc1fe007d search: Actually crash when seeing errors from a native search provider
We don't implement many of these, and not catching the error lets us
see stack traces and other such information a lot faster.

https://bugzilla.gnome.org/show_bug.cgi?id=704912
2013-10-29 16:02:32 -04:00
c0b45c9fc4 Remove wanda
It's been broken for quite a bit since we removed Panel.Animation,
and hasn't really ever worked with our new search results. It's also
the only non-remote provider left.

Maybe we'll add it back as a remote provider later, but for now, just
ditch it.
2013-10-29 15:46:49 -04:00
41315f45a9 notificationDaemon: Fix custom icons
The 'icon' property contains a serialized GIcon, so we need to
deserialize it when setting the icon.

https://bugzilla.gnome.org/show_bug.cgi?id=710596
2013-10-28 12:28:37 +01:00
04d28a0eea notificationDaemon: Fix urgency hint
We currently mark notifications as urgent which merely contain the
'urgent' property, even when set to false. Look at the actual value
instead.

https://bugzilla.gnome.org/show_bug.cgi?id=710596
2013-10-28 12:28:37 +01:00
1e9cd3f785 Updated Brazilian Portuguese translation
Fixes "Off" translation
2013-10-27 15:46:38 -02:00
f462dd6a4c Updated Hebrew translation
Maybe DL not updated?
2013-10-27 16:40:28 +02:00
34e75fc595 notificationDaemon: Fix button parameter name
Gio ended up using 'target' rather than 'action-target'.

https://bugzilla.gnome.org/show_bug.cgi?id=710596
2013-10-27 11:38:36 +01:00
dac513e046 notificationDaemon: Unpack button label
deep_unpack() doesn't unpack as deeply as one might hope ...

https://bugzilla.gnome.org/show_bug.cgi?id=710596
2013-10-27 11:38:36 +01:00
2c538d247b catch more errors on extensions enable() and disable()
https://bugzilla.gnome.org/show_bug.cgi?id=688331
2013-10-26 16:35:47 +02:00
f3b7f61e54 AltTabSwitcherPopup: check the number of items after creating the popup child
The popup can be empty if the alt-tab switcher is configured
in workspace-only mode, even if there are applications running.

https://bugzilla.gnome.org/show_bug.cgi?id=710745
2013-10-26 16:29:27 +02:00
d47ecf19f5 need space between item in endsession dialog session-list and app-list
https://bugzilla.gnome.org/show_bug.cgi?id=710543
2013-10-26 16:24:41 +02:00
da19b344b5 RemoteSearch: don't autostart dbus search providers at login
Use the new glib flag that allows us to create the proxy immediately
but only activate the service when making the first call.

https://bugzilla.gnome.org/show_bug.cgi?id=708830
2013-10-26 15:20:52 +02:00
eb66407926 NotificationDaemon: fix more fallout
Missing Gtk import for action-icon buttons.

https://bugzilla.gnome.org/show_bug.cgi?id=710596
2013-10-26 14:56:50 +02:00
61c5b8e7d2 notificationDaemon: Pass the correct id to makeButton()
The function expects the action's ID, not the notification's one.

https://bugzilla.gnome.org/show_bug.cgi?id=710596
2013-10-25 14:15:10 +01:00
4b09d57ec2 Fix fallout from notification changes
Commit 5f081b8f8d moved code without moving a helper function
used.

https://bugzilla.gnome.org/show_bug.cgi?id=710596
2013-10-25 14:15:10 +01:00
a16f699dfc theme: Add some vertical padding on login screen
This was apparently lost during some rewrite this cycle ...

https://bugzilla.gnome.org/show_bug.cgi?id=710555
2013-10-25 14:15:10 +01:00
b908a3d70a Stringify the xml definitions for E4X removal
https://bugzilla.gnome.org/show_bug.cgi?id=691409
2013-10-25 08:57:27 +11:00
d519c7263e Background: Drop "saturation" related source
"saturation" was removed from MetaBackground in mutter with
https://git.gnome.org/browse/mutter/commit/?id=0e589061
2013-10-23 21:37:27 +02:00
5dedc5d8ba shell-app: Remove old unused functionality 2013-10-21 17:49:46 -04:00
3ca1784ff4 notificationDaemon: Fix activating with a target
Targets should not be unpacked, and action IDs should.
2013-10-21 16:50:18 -04:00
b54d512f3f notificationDaemon: Fix loading/saving of notifications 2013-10-21 16:14:14 -04:00
9d8fb19f55 notificationDaemon: Write notifications out to disk
This allows notifications to persist even after reboots and
gnome-shell restarts.

https://bugzilla.gnome.org/show_bug.cgi?id=710137
2013-10-21 14:13:21 -04:00
1ac4ab7edc shell-global: Add new APIs for saving/loading persistent state
https://bugzilla.gnome.org/show_bug.cgi?id=710137
2013-10-21 14:13:21 -04:00
c9b73ac731 shell-global: Handle empty variants better
In cases where we have an array of 0 elements or similar, the
data returned may be NULL. Since g_file_replace_contents will
assert in this case, simply check for this and delete the file
instead.

https://bugzilla.gnome.org/show_bug.cgi?id=710137
2013-10-21 14:13:21 -04:00
e0b87f1e14 notificationDaemon: Implement the new GTK+ notifications API
The new API is designed to support features like persistence and uses
the new org.freedesktop.Application specification for activating
actions on notifications. While we won't add support for persistence
yet, implement the new notification spec with parity of the old one.

https://bugzilla.gnome.org/show_bug.cgi?id=710137
2013-10-21 14:12:47 -04:00
394743efc8 notificationDaemon: Rename the existing implementation to the FdoNotificationSource
We'll add a new, simpler private implementation that's used by the new
GNotification API in gio.

https://bugzilla.gnome.org/show_bug.cgi?id=710137
2013-10-21 12:30:25 -04:00
27a86a4756 notificationDaemon: Move nextNotificationId inside the daemon class
This won't be used by the new notification daemon.

https://bugzilla.gnome.org/show_bug.cgi?id=710137
2013-10-21 12:30:25 -04:00
8ee0ef2cde messageTray: Don't always open the source when clicking on the notification
Some consumers may not always want to open the app, so make clients that
want to do this explicitly connect to the 'clicked' signal.

https://bugzilla.gnome.org/show_bug.cgi?id=710137
2013-10-21 12:30:25 -04:00
43f4682ec4 messageTray: Make addButton/addAction take a callback
This is a much simpler API for consumers to manage.

https://bugzilla.gnome.org/show_bug.cgi?id=710137
2013-10-21 12:30:25 -04:00
5f081b8f8d messageTray: Remove useActionIcons feature
This can be put in the legacy notification daemon with the new
addButtonFull API, to create icon names for actions.

https://bugzilla.gnome.org/show_bug.cgi?id=710137
2013-10-21 12:30:25 -04:00
5023542882 messageTray: Split out addButton to allow consumers to pass a pre-made button
Some consumers may want to construct their buttons specially, so allow them
to do that by adding a new API that takes a button instead of a label.

https://bugzilla.gnome.org/show_bug.cgi?id=710137
2013-10-21 12:30:25 -04:00
88d0731d80 messageTray: Replace setButtonSensitive by simply returning the button
We want to remove 'id's from buttons, and simply returning the button actor
is more powerful anyway.

https://bugzilla.gnome.org/show_bug.cgi?id=710137
2013-10-21 12:30:25 -04:00
06cb8c52d7 Updated Hebrew translation 2013-10-20 19:52:43 +03:00
4d1358b7ed st-widget: Use g_clear_pointer
https://bugzilla.gnome.org/show_bug.cgi?id=710541
2013-10-20 14:25:16 +02:00
0cfa7c1c56 power: Fix undefined variables
A couple of variables that should have been gathered from the proxy
and weren't.
2013-10-19 18:00:08 +02:00
76928390a3 Updated Russian translation 2013-10-18 18:30:12 +04:00
af1f9cd76d network: Don't use StButtons for items in selector
Their use blocks activation of the default button by keyboard, which
is important for accessibility. Use a Clutter.ClickAction instead,
which doesn't have this problem as it only considers mouse events.

https://bugzilla.gnome.org/show_bug.cgi?id=710144
2013-10-17 19:46:30 +02:00
2f39f3d146 Revert "network: being able to use keyboard to connect to a Wireless"
This reverts commit d175a588f7.

https://bugzilla.gnome.org/show_bug.cgi?id=710144
2013-10-17 19:46:29 +02:00
f72f39bc26 messageTray: Only try to focus the summary when summoning the tray by <Super>M
Otherwise, when closing the tray, we'll try to focus an actor, which will
focus the stage window, which will drop the focus from whatever window we
already had focused.

https://bugzilla.gnome.org/show_bug.cgi?id=710347
2013-10-17 13:07:42 -04:00
2659ba6bb4 power: Fix typo
JS ERROR: ReferenceError: this_proxy is not defined
Indicator<._getStatus@/usr/share/gnome-shell/js/ui/status/power.js:66
2013-10-17 18:12:02 +02:00
0b8c0c202e power: Use UPower directly instead of gnome-settings-daemon
UPower master exports a display device that can be used to
compute whether to show a status icon, and what we should show.

https://bugzilla.gnome.org/show_bug.cgi?id=710273
2013-10-17 17:36:27 +02:00
37c8132632 workspace: Adapt to mutter API changes 2013-10-15 15:46:36 -04:00
02f2f694e4 Bump version to 3.10.1
Update NEWS.
2013-10-15 20:27:52 +02:00
d175a588f7 network: being able to use keyboard to connect to a Wireless
https://bugzilla.gnome.org/show_bug.cgi?id=710144
2013-10-15 20:27:05 +02:00
4bb41f2f66 Revert "network: being able to use keyboard to connect to a Wireless"
This reverts commit d581d29198.
2013-10-15 20:14:02 +02:00
088c46c7be Revert "Bump version to 3.10.1"
This reverts commit 4228c40b3d.
2013-10-15 20:14:00 +02:00
4228c40b3d Bump version to 3.10.1
Update NEWS.
2013-10-15 20:06:25 +02:00
d581d29198 network: being able to use keyboard to connect to a Wireless
https://bugzilla.gnome.org/show_bug.cgi?id=710144
2013-10-15 19:10:38 +02:00
5a7e854f9e appDisplay: Remember selected view across sessions
The application picker will always open with the view that was last
selected during the session, but the selection is reset on each
restart. This results in some annoyance for users that use the
ALL view exclusively, as they have to toggle views once each
session - the same would apply to exclusive FREQUENT view users
were the defaults to be changed, so the best solution is to simply
make the selected view persistent by storing it in GSettings.

https://bugzilla.gnome.org/show_bug.cgi?id=710042
2013-10-15 19:06:53 +02:00
6e13823ccc shell-js: fix incorrect G_BEGIN_DECLS in header 2013-10-15 09:21:59 +11:00
5c5f2fdf8f a11y: setting ATK_ROLE_ARROW to object unicodeArrow
https://bugzilla.gnome.org/show_bug.cgi?id=710120
2013-10-14 22:29:09 +02:00
96aa33f4ef messageTray: Don't remove and re-add the focus group on button changes
https://bugzilla.gnome.org/show_bug.cgi?id=710115
2013-10-14 15:24:53 -04:00
25fd23e703 messageTray: Split out the notification's destroy handler
This is complex enough to split out.

https://bugzilla.gnome.org/show_bug.cgi?id=710115
2013-10-14 15:24:53 -04:00
99cf4e5787 messageTray: Only connect to a notification's open/destroy once
If we pushNotification the same notification multiple times, we
won't append it to the array again, but we will attach multiple
handlers needlessly.

https://bugzilla.gnome.org/show_bug.cgi?id=710115
2013-10-14 15:24:53 -04:00
66a4cb5875 messageTray: Move the notification policy classes here
The NotificationDaemon really should be for the hookup of remote
notifications, rather than policies.

https://bugzilla.gnome.org/show_bug.cgi?id=710115
2013-10-14 15:24:53 -04:00
da14e2c349 messageTray: Clean up code that determines if something is clearable
https://bugzilla.gnome.org/show_bug.cgi?id=710115
2013-10-14 15:24:53 -04:00
4cda61a16a gdm: support pre-authenticated logins from oVirt
oVirt is software for managing medium-to-large scale deployments of
virtual machine guests across multiple hosts. It supports a feature
where users can authenticate with a central server and get
transparently connected to a guest system and then automatically get logged
into that guest to an associated user session.

Guests using old versions of GDM support this single-sign-on capability
by means of a greeter plugin, using the old greeter's extension
API.

This commit adds similar support to the gnome-shell based login screen.

How it works:

* The OVirtCredentialsManager singleton listens for

  'org.ovirt.vdsm.Credentials.UserAuthenticated'

  D-Bus signal on the system bus from the

  'org.ovirt.vdsm.Credentials'

  bus name. The service that provides that bus name is called
  the oVirt guest agent. It is also responsible for interacting
  with the the central server to get user credentials.

* This UserAuthenticated signal passes, as a parameter, the a token
  which needs to be passed through to the PAM service that is specifically
  set up to integrate with the oVirt authentication architecture.
  The singleton object keeps the token internally so it can be queried
  later on.

* The OVirtCredentialsManager emits a signal 'user-authenticated' on
  it's object once the dbus signal is triggered

* When the 'user-authenticated' signal is emitted, the login screen
  tells GDM to start user verification using the PAM service. The
  authentication stack of the service includes a PAM module
  provided by oVirt that securely retrieves user credentials
  from the oVirt guest agent. The PAM module then forwards those
  credentials on to other modules in the stack so, e.g.,
  the user's gnome keyring can be automatically unlocked.

* In case of the screen shield being visible, it also will react on that
  'user-authenticated' signal and lift the shield.
  In that case the login screen will check on construction time if
  the signal has already been triggered, and a token is available.
  If a token is available it will immediately trigger the functionality
  as described above.

Signed-off-by: Vinzenz Feenstra <evilissimo@redhat.com>

https://bugzilla.gnome.org/show_bug.cgi?id=702162
2013-10-14 13:54:30 -04:00
002afda503 SearchDisplay: handle certain result IDs specially
Allow the prefix 'special:' applied to result IDs to mark results
that should be always shown, even when they would overflow the
maximum results cap. This will be used by epiphany for the special
"Search the Web" result.

https://bugzilla.gnome.org/show_bug.cgi?id=707055
2013-10-14 18:47:20 +02:00
cb7a2e8c6a shell-global: Use G_VARIANT_TYPE macro for checking 2013-10-14 10:52:45 -04:00
d21ae1dad1 shell-global: Fix an invalid memory botch-up
The value we return is a floating ref, which gjs shouldn't
try to free.

https://bugzilla.gnome.org/show_bug.cgi?id=710104
2013-10-14 09:49:51 -04:00
4548859509 shell-global: Fix a few memory leaks
https://bugzilla.gnome.org/show_bug.cgi?id=710104
2013-10-14 09:49:51 -04:00
09c6e6427a Updated Serbian translation 2013-10-14 10:29:57 +02:00
8c34398a15 Updated Slovenian translation 2013-10-13 23:43:34 +02:00
a65b705080 [l10n] Updated Catalan (Valencian) translation 2013-10-13 22:43:34 +02:00
7e28c71074 [l10n] Update Catalan translation 2013-10-13 22:43:31 +02:00
726f4a6715 Updated Lithuanian translation 2013-10-13 20:06:00 +03:00
a444b43548 Updated Portuguese translation 2013-10-13 12:22:54 +01:00
473bb139d1 Updated Latvian translation 2013-10-13 13:35:18 +03:00
d084770cea NetworkMenu: fix regression for VPN connections
Fix regression from e898e29910,
the code was moved from a method to a global function, but one
call site was not updated.

https://bugzilla.gnome.org/show_bug.cgi?id=710019
2013-10-13 02:11:14 +02:00
619389ed20 NotificationDaemon: fix icons for notifications without an app
NotificationDaemon doesn't pass a gicon to the Notification constructor,
because it calls .update() immediately after, so messageTray.js
calls into Source.createIcon(), which returns null and crashes.
Instead, shortcut the Notification constructor by skipping
.update() completely.

https://bugzilla.gnome.org/show_bug.cgi?id=709998
2013-10-13 01:23:18 +02:00
ad043e009e workspaceThumbnail: Drop the _background hack
The _background hack was added because the old way the zooming animation
worked, it set the allocation of the workspaces view and thumbnails box
to the final position and used animations to smoothly animate.

During the 3.6 cycle when we added the new search view, Cosimo changed the
way the zoom animation works so that rather than set the final allocation
and animate, we actually do adjust the allocation of the workspaces view
and thumbnails box.

So, as the hack is no longer necessary, we can drop it.

https://bugzilla.gnome.org/show_bug.cgi?id=694881
2013-10-12 14:38:13 -04:00
89b9d079b1 overviewControls: Don't use the child's preferred size to slide from
In order for the workspace thumbnails box to have the correct size,
we need to constrain the width of the thumbnails box to the height we're
given, instead of assuming an unlimited height.

https://bugzilla.gnome.org/show_bug.cgi?id=694881
2013-10-12 14:38:13 -04:00
58a8845047 overviewControls: Don't try to align something sliding to the right
0 is already the correct value.

https://bugzilla.gnome.org/show_bug.cgi?id=694881
2013-10-12 14:38:13 -04:00
3e6c8e68b4 overviewControls: Clarify some code with a comment
translationX is sort of a bad name, since it confuses with the
actor's translation, which is used for sliding without allocation.

https://bugzilla.gnome.org/show_bug.cgi?id=694881
2013-10-12 14:38:13 -04:00
40cd92f701 overviewControls: Correct the use of x2 in SlidingControl
The x2 here needs to be more than just the width; it needs to
be added onto the x1.

https://bugzilla.gnome.org/show_bug.cgi?id=694881
2013-10-12 14:38:13 -04:00
e216addf7c Updated Dutch translation 2013-10-12 19:59:34 +02:00
9291594330 app-system: remove outdated comment
This parameter was removed before 3.2 but the comment stuck around.
2013-10-12 10:09:36 -05:00
f6010864ea layout: Flush region update in showOverview
We cannot wait for the queued update region to fire when
xdnd is being used because a wrong input shape can result
into a xdnd leave event when the user moves the pointer fast.

https://bugzilla.gnome.org/show_bug.cgi?id=708887
2013-10-11 20:16:44 +02:00
13e6a6def5 Revert "overview: Ignore dragEnd while the animation is still in progress"
This reverts commit 80a3bb85aa.

https://bugzilla.gnome.org/show_bug.cgi?id=708887
2013-10-11 20:16:44 +02:00
575b373cd5 power: Use the icon from the primary device for status
https://bugzilla.gnome.org/show_bug.cgi?id=709925
2013-10-11 13:37:10 -04:00
0892065649 notificationDaemon: Raise the app when clicking on a notification, not the MRU window
https://bugzilla.gnome.org/show_bug.cgi?id=709866
2013-10-11 13:37:10 -04:00
766ef367fb notificationDaemon: Fix the fallback for image-data
The indentation here is wrong, the else actually belongs to the outer
if statement, not the inner if statement.
2013-10-11 13:37:10 -04:00
0c7d9958f5 notificationDaemon: Remove dead code 2013-10-11 13:37:09 -04:00
425b8f6073 Updated Czech translation 2013-10-11 18:28:52 +02:00
63593e45a6 Make dropdown arrows consistent size
Since the agregate menu does 120% of font-size, make this
for all dropdown arrows in gnome-shell and rename the css
class to make clear that it is used in overall gnome-shell

https://bugzilla.gnome.org/show_bug.cgi?id=709564
2013-10-11 17:32:55 +02:00
a6ee9806de Uploaded Ukranian 2013-10-11 15:02:28 +03:00
a6579f4ceb Updated Brazilian Portuguese translation 2013-10-11 07:24:18 -03:00
639622a4fe [l10n] Updated Italian translation. 2013-10-11 09:41:24 +02:00
e28ec2f5ab Updated Indonesian translation 2013-10-11 10:15:14 +07:00
873753c735 Updated Hebrew translation 2013-10-11 01:14:45 +03:00
ff14951be4 Updated Hebrew translation 2013-10-11 01:11:25 +03:00
b47b445558 Updated Polish translation 2013-10-10 23:43:39 +02:00
6e9a2fea89 messageTray: Add 'Notifications' switch to tray menu
According to the designs, the notifications switch was supposed
to move from the user menu to the new message tray menu. However
so far the new system status implementation only removed the old
switch, so add it back in its new place now.

https://bugzilla.gnome.org/show_bug.cgi?id=707073
2013-10-10 23:13:47 +02:00
64d2679b3c Updated Czech translation 2013-10-10 21:09:42 +02:00
3c3d4dfccb Updated Hungarian translation 2013-10-10 13:43:23 +02:00
0ccffed517 Updated Spanish translation 2013-10-10 10:49:53 +02:00
20c18c1fc0 Suppress animation for titles in overview
Because of the animation and collision with relayout, the title of windows in overview may not appear, mainly
the first time we enter in overview

With an animation delay of 0.1s, you'll not see the difference

https://bugzilla.gnome.org/show_bug.cgi?id=709392
2013-10-10 08:51:13 +02:00
a21c0097c2 Tajik translation updated 2013-10-10 11:48:03 +05:00
404d9ef2af [l10n] Updated Italian translation. 2013-10-10 08:37:21 +02:00
503fa1cbce Updated Indonesian translation 2013-10-10 12:59:23 +07:00
63f7991d0f Updated Brazilian Portuguese translation 2013-10-09 23:26:40 -03:00
ae301c1f39 Updated Galician translations 2013-10-09 23:09:57 +02:00
8e911fb719 Updated Galician translations 2013-10-09 23:04:21 +02:00
a0d84e44c5 L10N: Updated Persian translations 2013-10-10 00:28:17 +03:30
81fb7ebb31 Fix some broken GTK-Doc comment blocks 2013-10-09 20:14:57 +02:00
3751211590 shell-global: Fix introspection issues
Remove stray bullet points, and don't actually mark for introspection
since it's private anyway (it starts with a _)
2013-10-09 13:31:00 -04:00
6b4cba09be Updated Polish translation 2013-10-09 19:04:21 +02:00
6a7d184b7b NetworkMenu: make the settings launcher point to the right devices
Tell gnome-control-center to focus on the associated device when
launching it from one of the submenus.

https://bugzilla.gnome.org/show_bug.cgi?id=709246
2013-10-09 18:39:44 +02:00
bde5cfc8bb NetworkMenu/NMDeviceModem: use the operator name when we have it
Instead of the connection name, as the operator name is usually
shorter and more useful.

https://bugzilla.gnome.org/show_bug.cgi?id=709043
2013-10-09 18:39:44 +02:00
777c7a952b NetworkMenu/NMDeviceModem: fix status texts for airplane mode
Show "Hardware Disabled" when disabled by HW switch, and
generically "Disabled" when airplane mode is active, as
indicated by v4 mockups.
Note that bluetooth is not affected by NM handling of airplane
mode (and generally the firmware makes the USB bluetooth
adapter disappear when rfkilled), so this is in NMDeviceModem
instead of NMConnectionDevice.

https://bugzilla.gnome.org/show_bug.cgi?id=709043
2013-10-09 18:39:44 +02:00
c79bdd9029 NetworkMenu/NMConnectionDevice: fix status text
Be consistent with v4 mockups

https://bugzilla.gnome.org/show_bug.cgi?id=709043
2013-10-09 18:39:44 +02:00
6a154efe65 Network: show Not Connected instead of Off when wifi radio is on
When wifi is powered but not connected, show Not Connected instead
of off. This avoids a Off status next to a Turn Off menu item.

https://bugzilla.gnome.org/show_bug.cgi?id=709043
2013-10-09 18:39:44 +02:00
627f3ef36b keyring: Cancel active prompts on disable()
Since commit 1242a16265, we will use a fake prompt which
cancels alls requests without dialog when the keyring component
is disabled. However this does only apply to new requests, dialogs
that are already active when the session mode changes are kept
open. This is not quite as expected, so cancel the prompt in that
case.

https://bugzilla.gnome.org/show_bug.cgi?id=708910
2013-10-09 17:58:37 +02:00
3d28836f2c keyring: Remove unused global
https://bugzilla.gnome.org/show_bug.cgi?id=708910
2013-10-09 17:58:37 +02:00
c28bd04958 Finnish translation update 2013-10-09 14:23:48 +03:00
a6fb3acb42 NetworkMenu: hide the "Turn On" item when the rfkill is hardware blocked
If wifi is disabled in hardware, there is nothing we can do at
the sw level, so hide the menu item.

https://bugzilla.gnome.org/show_bug.cgi?id=709635
2013-10-08 22:20:27 +02:00
8a8b3bf96e NetworkMenu: fix wireless-enabled property notifications
The property is on the NMClient, not NMDevice. Also, make sure
we disconnect the signal when the item is destroyed.
Also, connect to wireless-hardware-enabled, which we'll use soon.

https://bugzilla.gnome.org/show_bug.cgi?id=709635
2013-10-08 22:20:27 +02:00
f7624e5f05 network: make the icon visible again when going from ethernet to nothing
https://bugzilla.gnome.org/show_bug.cgi?id=709638
2013-10-08 14:26:38 -04:00
55edfd2e4a notificationDaemon: Focus the new window before destroying the notification
Destroying the notification will make the key focus be reset to NULL, which
means that gnome-shell will try to focus the MRU window, thinking the user is
done interacting and wants to go back to whatever they were doing.

Unfortunately, since we focus two windows at the same time, they will have
the same timestamp, meaning that the window that actually gets focused will
be a race as to whoever responds to their WM_TAKE_FOCUS event last.

If we explicitly set the focus beforehand, then gnome-shell will believe it
got key focus taken away from it, and won't try to focus the MRU when the
key focus drops to NULL.

https://bugzilla.gnome.org/show_bug.cgi?id=703265
2013-10-08 13:05:34 -04:00
91878dd52c Updated Indonesian translation 2013-10-08 17:07:23 +07:00
c132358ec9 Updated Latvian translation 2013-10-07 21:54:39 +03:00
e426f8ac47 BoxPointer: account for border width when requesting size
We must reduce the forWidth in the call to get_preferred_height()
with the border width, otherwise we might request a smaller height
that we actually need and overflow.

https://bugzilla.gnome.org/show_bug.cgi?id=696564
2013-10-07 20:18:48 +02:00
740dca8afc build: Bump gjs requirement
https://bugzilla.gnome.org/show_bug.cgi?id=709543
2013-10-07 09:37:59 +02:00
6ae06d319c [l10n] Updated Catalan (Valencian) translation 2013-10-06 21:58:43 +02:00
30bc2b2f9c [l10n] Update Catalan translation 2013-10-06 21:06:39 +02:00
7dc8e9d657 Updated Russian translation 2013-10-06 12:31:47 +04:00
1ab2fa5bf0 [l10n]Updated Turkish translation 2013-10-06 09:19:37 +03:00
779a1077bf Updated Serbian translation 2013-10-05 20:59:51 +02:00
815bf7a53a L10N: Updated Persian translations 2013-10-05 18:06:59 +03:30
91a382dfb5 Fix Lithuanian calendar translation 2013-10-05 16:51:19 +03:00
2d8d4cd57d Updated Serbian translation 2013-10-05 06:49:07 +02:00
0c5fe2b3bf Updated Serbian translation 2013-10-05 06:08:07 +02:00
56c487347f Updated Belarusian translation. 2013-10-03 23:02:05 +03:00
0a572fce1b Updated Norwegian bokmål translation 2013-10-03 20:38:08 +02:00
e898e29910 NetworkMenu/NMConnectionDevice: grab the connection before using it
If the active connection for the device is not the primary or
activating globally, it won't have the _connection and _primaryDevice
expando properties, so grab them from the settings object.

https://bugzilla.gnome.org/show_bug.cgi?id=709043
2013-10-03 16:03:41 +02:00
843f076225 NetworkMenu/NMDeviceWireless: use the device state instead of active connection state
Since we connect to signals for that, we need to sync on that, or
we can miss a notification.

https://bugzilla.gnome.org/show_bug.cgi?id=709043
2013-10-03 16:01:27 +02:00
fdb732c8c2 Bluetooth: fix ngettext call
https://bugzilla.gnome.org/show_bug.cgi?id=709043
2013-10-03 16:01:27 +02:00
33896a4e8f network: fix NMConnectionSection destruction
The patch fixes the following warning, and along with it, the proper
destruction of the NMConnectionSection is performed so that items get
correctly removed from the menu.

(gnome-shell:24528): Gjs-WARNING **: JS ERROR: TypeError:
this.statusItem is undefined
NMConnectionSection<.destroy@/home/aleksander/gnome/install/share/gnome-shell/js/ui/status/network.js:173
wrapper@/home/aleksander/gnome/install/share/gjs-1.0/lang.js:213
_parent@/home/aleksander/gnome/install/share/gjs-1.0/lang.js:175
NMConnectionDevice<.destroy@/home/aleksander/gnome/install/share/gnome-shell/js/ui/status/network.js:292
wrapper@/home/aleksander/gnome/install/share/gjs-1.0/lang.js:213
_parent@/home/aleksander/gnome/install/share/gjs-1.0/lang.js:175
NMDeviceModem<.destroy@/home/aleksander/gnome/install/share/gnome-shell/js/ui/status/network.js:448
wrapper@/home/aleksander/gnome/install/share/gjs-1.0/lang.js:213
NMApplet<._removeDeviceWrapper@/home/aleksander/gnome/install/share/gnome-shell/js/ui/status/network.js:1421
wrapper@/home/aleksander/gnome/install/share/gjs-1.0/lang.js:213
NMApplet<._deviceRemoved@/home/aleksander/gnome/install/share/gnome-shell/js/ui/status/network.js:1416
wrapper@/home/aleksander/gnome/install/share/gjs-1.0/lang.js:213

https://bugzilla.gnome.org/show_bug.cgi?id=709248
2013-10-03 00:50:02 +02:00
51e016a0d6 theme: modify the avatar and user name
Change the avatar and user name border radius, padding
and text size to be consistent with mockups.

https://bugzilla.gnome.org/show_bug.cgi?id=702309
2013-10-02 22:26:30 +02:00
8737b06559 loginDialog: MessageType is now in GdmUtil
https://bugzilla.gnome.org/show_bug.cgi?id=709286
2013-10-02 13:58:09 -04:00
15ab285174 layout: Use monitor index when adding bg managers
Don't assume that this._bgManagers.push() (i.e adding to the end) is always
correct.

On startup we call _createPrimaryBackground which passes in the primary index
which may not be 0.
2013-10-02 15:58:22 +02:00
3a4782cc64 app-system: Fix some enum warnings
https://bugzilla.gnome.org/show_bug.cgi?id=698486
2013-10-02 09:49:03 -04:00
0256a6d47b app: Use g_variant_lookup instead of dict iteration 2013-10-02 09:49:02 -04:00
8b0e846e0e background: Disconnect settings signal handler on destroy
We connect to the changed signal in _init() but never actually disconnect from
it. The callback has a reference to "this" which results into the background
object not getting garbage collected.

Fix that leaks by disconnecting in _destroy()

https://bugzilla.gnome.org/show_bug.cgi?id=709263
2013-10-02 15:43:30 +02:00
41acb5d3cc Updated Czech translation 2013-10-02 09:56:45 +02:00
a2f9b8ea9b workspacesView: Remove dead code 2013-10-01 18:41:26 -04:00
6237a1c505 workspace: Remove unused leftover constant
from the window zooming days
2013-10-01 18:41:26 -04:00
7c08db0b0f [l10n] Updated Italian translation. 2013-10-01 19:22:11 +02:00
df1270ac49 Updated Danish translation 2013-10-01 19:10:05 +02:00
46edc053d4 overview: Add cover pane directly to overviewGroup
The cover pane is used to block events during transitions, but as
workspaces don't share the same container as other overview elements,
they are currently excempt from the event blocking.
Move the cover pane to the top-level overview container instead.

https://bugzilla.gnome.org/show_bug.cgi?id=709034
2013-10-01 17:16:51 +02:00
908046c31a [l10n] Updated Estonian translation 2013-10-01 17:14:01 +03:00
8380c79875 network: Update the network indicator when the VPN state changes 2013-10-01 10:25:46 -04:00
8a4879a96a Updated Traditional Chinese translation(Hong Kong and Taiwan) 2013-09-30 19:40:48 +08:00
cdf1a77f08 Updated Spanish translation 2013-09-30 12:00:58 +02:00
3f9857ccbd Assamese translation updated 2013-09-30 13:32:07 +05:30
1d65a31420 Updated Lithuanian translation 2013-09-29 20:33:17 +03:00
dafdf0838a Updated Slovak translation 2013-09-29 17:41:47 +01:00
f9cf135f68 update Punjabi Translaiton 2013-09-29 05:26:34 -05:00
a1878e54c9 Updated Norwegian bokmål translation 2013-09-29 11:29:08 +02:00
95e5d899a9 Updated Basque language 2013-09-29 10:53:34 +02:00
ee8321df67 Updated Slovenian translation 2013-09-28 21:49:04 +02:00
4918213e68 Tajik translation updated 2013-09-28 19:39:12 +05:00
ed7f349fc6 Updated Galician translations 2013-09-28 16:25:15 +02:00
2888f22a24 Updated Norwegian bokmål translation 2013-09-28 14:53:19 +02:00
fcb217f681 Updated Brazilian Portuguese translation 2013-09-28 05:16:53 -03:00
9ffa9fe1a8 Updated Galician translations 2013-09-27 21:38:08 +02:00
905020c507 Updated Slovak translation 2013-09-27 17:40:35 +01:00
02f5500641 theme: Make indicators checked&active like checked 2013-09-27 17:06:26 +02:00
465af55d6e theme: Add hover/active states to indicators
Until now we had the same svg for hover, active and checked
states in the pagination indicators. Just differentiate between
them using differents svg.

svg files provided by Jakub Steiner

https://bugzilla.gnome.org/show_bug.cgi?id=708852
2013-09-27 15:50:28 +02:00
80a3bb85aa overview: Ignore dragEnd while the animation is still in progress
Moving the mouse fast enough during xdnd will trigger a xdnd-leave event
because the input shape is not updated until after the animation is done.

So simply ignore the leave events while the animation is in progress.

https://bugzilla.gnome.org/show_bug.cgi?id=708887
2013-09-27 12:54:22 +02:00
ea26bd3003 Updated Latvian translation 2013-09-26 23:30:59 +03:00
508a511d2a Revert "overviewControls: Don't allow appearing controls to "pop in""
This reverts commit e31693bbee.

This doesn't properly adjust the allocation, leading to an unbalanced
overview where things aren't centered properly. Just revert for now,
and we'll rethink this next cycle.
2013-09-26 16:20:06 -04:00
2d80cb71db searchDisplay: Scroll results when the user focuses a provider icon
Otherwise, it seems odd to highlight results the user can't see
simply by pressing down a lot.

https://bugzilla.gnome.org/show_bug.cgi?id=708868
2013-09-26 15:17:29 -04:00
e31693bbee overviewControls: Don't allow appearing controls to "pop in"
When coming back from search or apps, the workspace thumbnails and dash
don't slide in but "pop in". This is because of bad timing: when slideIn
is called, we immediately start the translation animation, and it
completes before by the time we fade the new page in.

Fix this by calling slideIn and slideOut at two different times: we now
slide out when the old page with our controls is fading out, and slide in
when the new page with our controls is fading in.

https://bugzilla.gnome.org/show_bug.cgi?id=708340
2013-09-26 13:39:20 -04:00
fb561f10a7 network: Make sure to set the signal icon at dialog item construction time
We forgot to set it here; it would be updated if there was changes in the
signal, but not when it was created.

https://bugzilla.gnome.org/show_bug.cgi?id=708442
2013-09-26 11:06:42 -04:00
0c57d53e03 Update Esperanto translation 2013-09-26 09:09:37 -04:00
3b1b9f589b SystemMenu: wait for a completed paint before switching VT
Activating the GDM login screen switches VT and causes X to freeze
event processing (because it lost the drm master), so must make
sure to have painted the lock screen at least once before proceeding,
or the user can go back and see the unlocked desktop.

https://bugzilla.gnome.org/show_bug.cgi?id=708051
2013-09-26 10:46:42 +02:00
ac8d39acf4 Updated Lithuanian translation 2013-09-25 23:22:27 +03:00
664e795217 [l10n]Updated Turkish translation 2013-09-25 21:16:28 +03:00
82bf323f63 Updated German translation 2013-09-25 15:25:31 +02:00
547ac85113 Updated Basque language 2013-09-25 11:17:01 +02:00
263 changed files with 48515 additions and 44452 deletions

5
.gitignore vendored
View File

@ -43,6 +43,8 @@ docs/reference/*/xml/
docs/reference/shell/doc-gen-*
gtk-doc.make
js/misc/config.js
js/js-resources.c
js/js-resources.h
intltool-extract.in
intltool-merge.in
intltool-update.in
@ -76,11 +78,8 @@ src/gnome-shell-calendar-server
src/gnome-shell-extension-prefs
src/gnome-shell-extension-tool
src/gnome-shell-hotplug-sniffer
src/gnome-shell-jhbuild
src/gnome-shell-perf-helper
src/gnome-shell-perf-tool
src/gnome-shell-real
src/gnome-shell-wayland
src/hotplug-sniffer/org.gnome.Shell.HotplugSniffer.service
src/run-js-test
src/test-recorder

41
COPYING
View File

@ -1,12 +1,12 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
@ -225,7 +225,7 @@ impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
@ -303,17 +303,16 @@ the "copyright" line and a pointer to where the full notice is found.
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
@ -336,5 +335,5 @@ necessary. Here is a sample; alter the names:
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

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 docs
SUBDIRS = data js src tests po docs
if BUILD_BROWSER_PLUGIN
SUBDIRS += browser-plugin
endif
if ENABLE_MAN
SUBDIRS += man

342
NEWS
View File

@ -1,3 +1,345 @@
3.13.2
======
* Make airplane mode menu insensitive in lock screen [Giovanni; #729224]
* Don't always extend struts to the screen edge [Florian; #683819]
* Fix keynav for alternatives in AltSwitcher [Florian; #727259]
* Implement window menus in the shell [Jasper; #726352]
* Support resource:/// URLs in GNOME_SHELL_JS envvar [Owen; #730409]
* Fix switcher popups with keybindings containing Escape [Rui; #730739]
* Update extension-prefs UI to follow GNOME 3 patterns [Florian; #730829]
* Add support for fallback app menu in window decorations [Florian; #730752]
* Fix keynav escaping open app folders [Florian; #726760]
* Misc. bug fixes [Kalev, Florian, Owen; #729429, #728449, #730408, #730753,
#730653]
Contributors:
Giovanni Campagna, Piotr Drąg, Kalev Lember, Rui Matos, Florian Müllner,
Vadim Rutkovsky, Carlos Soriano, Jasper St. Pierre, Owen W. Taylor
Translations:
Ihar Hrachyshka [be], Giovanni Campagna [it], Carles Ferrando [ca@valencia],
Daniel Mustieles [es], Aurimas Černius [lt], Enrico Nicoletto [pt_BR],
Yosef Or Boczko [he], Marek Černocký [cs], Muhammet Kara [tr],
Georges Neto [pt_BR], Andika Triwidada [id]
3.13.1
======
* Ensure the currently focused app icon is viewable [Rui; #726759]
* Improve language in location menu [Zeeshan; #726498]
* Improve HiDpi support [Cosimo; #726907]
* Set accessible role for window previews [Alejandro; #726670]
* Fix bad antialiasing on panel menu buttons [Carlos; #727336]
* Don't hide location menu [Zeeshan; #727398]
* Fix IM candidate window obscuring current text [Rui; #727579]
* Don't always extend struts to the screen edge [Florian; #663690]
* Add shortcuts for switching to the last workspace [Elad; #659288]
* Show OSD window on all monitors [Adel; #722684]
* Improve consistency of labels in network menu [Paul; #727163]
* Fix zombie search providers showing up [Jasper; #728597]
* Remove ConsoleKit support [Florian; #686626]
* Fix region screenshots with open shell menus [Florian; #709126]
* Support <shift>insert in text entries [Florian; #648318]
* Improve app picker scrolling on touch [Jasper; #729064]
* Don't make date button clickable when on current date [Carlos; #726724]
* Tweak heuristic for hiding workspace switcher [Florian; #662457]
* Add option to show in Software to app context menu [Matthias; #643043]
* Misc. bug fixes and cleanups [Bastien, Florian, Giovanni, Adel, Vadim,
Carlos; #727983, #727948, #728512, #728681, #728897, #727384, #728820,
#715042, #728449, #728343]
Contributors:
Elad Alfassa, Zeeshan Ali (Khattak), Giovanni Campagna, Cosimo Cecchi,
Matthias Clasen, Piotr Drąg, Adel Gadllah, Paul Lange, Rui Matos,
Simon McVittie, Florian Müllner, Bastien Nocera, Alejandro Piñeiro,
Vadim Rutkovsky, Carlos Soriano, Jasper St. Pierre
Translations:
Khaled Hosny [ar], Piotr Drąg [pl], Yosef Or Boczko [he],
Antonio Fernandes C. Neto [pt_BR], Marek Černocký [cs], maria thukididu [el],
Andika Triwidada [id], Daniel Mustieles [es], Changwoo Ryu [ko],
Benjamin Steinwender [de], Sphinx Jiang [zh_CN],
Inaki Larranaga Murgoitio [eu], Marcus Lundblad [sv], Aurimas Černius [lt],
Stas Solovey [ru], Alexandre Franke [fr], Matej Urbančič [sl],
Fran Diéguez [gl], Pau Iranzo [ca], Luca Ferretti [it], Milo Casagrande [it],
Tiago S [pt], Victor Ibragimov [tg], Dirgita [id], Khoem Sokhem [km],
Rūdolfs Mazurs [lv], Balázs Úr [hu], Ask H. Larsen [da], Ikuya Awashiro [ja],
Wouter Bolsterlee [nl], Daniel Korostil [uk], Daniel Șerbănescu [ro],
Enrico Nicoletto [pt_BR]
3.12.0
======
* gdm: Reset greeter when coming back to login screen [Jasper; #726989]
Contributors:
Jasper St. Pierre
Translations:
Daniel Martinez [an], Yuri Myasoedov [ru], Inaki Larranaga Murgoitio [eu],
Abderrahim Kitouni [ar], Praveen Illa [te], Matej Urbančič [sl],
Chao-Hsiung Liao [zh_HK, zh_TW], Frédéric Péters [fr],
Мирослав Николић [sr, sr@latin], Ask H. Larsen [da], Kenneth Nielsen [da],
Jiro Matsuzawa [ja], Dušan Kazik [sk]
3.11.92
=======
* calendar: Grab key focus after changing day [Volker; #725606]
* gdm: Don't load user list if disabled [Florian; #725905]
* Don't show network-offline in the top bar [Jasper; #725340]
* Improve radial shade effect of modal dialogs [Giovanni; #725830]
* Fix broken suspend-on-idle functionality [Giovanni; #712706]
* Close wifi selection dialog when device disappears [Giovanni; #723935]
* Don't close chats when pressing Escape [Giovanni; #724178]
* Improve smartcard support in login/lock screen [Ray; #726262, #726263]
* Wake up screen when resuming from suspend [Giovanni; #726378]
* Make bluetooth and location items insensitive when locked [Florian; #726319]
* Don't show bluetooth icon when there is no adapter [Giovanni; #725057]
* Make sure to keep the OSK on top of modal dialogs [Rui; #719451]
* Misc. bug fixes and cleanups [Giovanni, Ray, Adel, Daniel, Jasper, Florian;
#725832, #725958, #722149, #724977, #724798, #725020, #723976, #726119,
#726238, #585500, #704844, #726323, #726322, #726120, #726414]
Contributors:
Giovanni Campagna, Daniel Drake, Adel Gadllah, Rui Matos, Florian Müllner,
Volker Sobek, Jasper St. Pierre, Ray Strode
Translations:
Fabio Tomat [fur], Rafael Ferreira [pt_BR], Fran Diéguez [gl],
Marek Černocký [cs], Baurzhan Muftakhidinov [kk], Andika Triwidada [id],
A S Alam [pa], Rūdolfs Mazurs [lv], Wylmer Wang [zh_CN],
Aurimas Černius [lt], Cheng-Chia Tseng [zh_TW], Stas Solovey [ru],
Tiagosdot [pt], Benjamin Steinwender [de], Frédéric Peters [fr],
Daniel Korostil [uk], Yaron Shahrabani [he], Ville-Pekka Vainio [fi],
maria thukididu [el], Victor Ibragimov [tg], Kjartan Maraas [nb],
Gábor Kelemen [hu], Ask H. Larsen [da]
3.11.91
=======
* Don't use network profile name in menu [Giovanni; #725586]
* calendar: Make date label clickable to return to current date [Vit; #641366]
* Misc. bug fixes [Florian, Zeeshan, Adel, Jasper, Dan, Volker; #724813,
#724686, #725082, #724870, #724779, #725533]
Contributors:
Zeeshan Ali (Khattak), Giovanni Campagna, Piotr Drąg, Adel Gadllah,
Florian Müllner, Volker Sobek, Vit Stanislav, Jasper St. Pierre, Dan Williams
Translations:
Victor Ibragimov [tg], Aurimas Černius [lt], Dimitris Spingos [el],
Andika Triwidada [id], Rafael Ferreira [pt_BR], Daniel Mustieles [es],
Baurzhan Muftakhidinov [kk], Marek Černocký [cs], Ihar Hrachyshka [be],
eternalhui [zh_CN], Yosef Or Boczko [he], Fran Diéguez [gl],
Khaled Hosny [ar], Ville-Pekka Vainio [fi], Piotr Drąg [pl],
Kjartan Maraas [nb], Changwoo Ryu [ko]
3.11.90
=======
* Stop showing two bluetooth entries [Giovanni; #709353]
* Improve styling of login/lock screen [Reda; #723833]
* Fix magnifier crosshairs [Magdalen; #723709]
* Make NetworkManager support optional [Michael; #669495]
* Make middle-click open a new instance [Florian; #695010]
* Scale the UI on high resolution displays [Cosimo, Adel; #705410, #724607]
* Remove notification counter on screen shield [Carlos; #709275]
* Improve app picker transition [Carlos; #722331]
* Add geolocation indicator to status menu [Zeeshan; #723684]
* Improve timestamps in chat notifications [Carlos; #708031, #715158]
* Improve network menus [Giovanni; #723570]
* Add "VPN Setting" item to VPN submenu [Giovanni; #709167]
* Improve appearance of disclosure arrows [Carlos; #720206]
* Add GSetting key to disable version validation of extensions [Adel; #724683]
* Delay auto-removing empty workspaces [Florian; #709064]
* Offer offline updates in the shutdown dialog [Kalev; #722898]
* Animate tile previews [Florian; #665758]
* Misc. bug fixes and cleanups [Giovanni, Ryan, Debarshi, Florian; #709128,
#722342, #723661, #724184, #724256, #724293, #724305, #722554, #724282,
#724690, #722928]
Contributors:
Zeeshan Ali (Khattak), Magdalen Berns, Michael Biebl, Giovanni Campagna,
Cosimo Cecchi, Adel Gadllah, Reda Lazri, Kalev Lember, Ryan Lortie,
Florian Müllner, Debarshi Ray, Carlos Soriano, Jasper St. Pierre,
Colin Walters
Translations:
Victor Ibragimov [tg], Daniel Mustieles [es], Khaled Hosny [ar],
Enrico Nicoletto [pt_BR], Yosef Or Boczko [he], Fran Diéguez [gl],
Marek Černocký [cs], Baurzhan Muftakhidinov [kk], Jorge Pérez Pérez [an],
Kjartan Maraas [nb], David Lüder [de], Daniel Korostil [uk], ngoswami [as],
Rafael Ferreira [pt_BR]
3.11.5
======
* Fix extension preference tool [Florian; #722334]
* Fix keyboard activation of legacy tray icons [Giovanni; #721267]
* Add radial background shade for modal dialogs [Giovanni; #669798]
* Show attached modal windows in the overview [Giovanni; #650843]
* Add support for desktop actions [Giovanni; #669603]
* Indicate in system status when location service is used [Zeeshan; #709372]
* Add support for extended app folder schema [Jasper; #723179]
* Show status icon for wired network connections [Jasper; #708966]
* Indicate airplane mode in network selection dialog [Giovanni; #709128]
* Misc bug fixes and cleanups [Florian, Sebastian, Giovanni, Tim, Matt, Jasper;
#722417, #722494, #722547, #722593, #722434, #722787, #722690, #722840,
#722660, #722812, #723197, #722927, #723306, #723308, #723523, #709685,
#723570]
Contributors:
Zeeshan Ali (Khattak), Magdalen Berns, Giovanni Campagna, William Jon McCann,
Sebastian Keller, Tim Lunn, Florian Müllner, Carlos Soriano,
Jasper St. Pierre, Rico Tzschichholz, Matt Watson
Translations:
Marek Černocký [cs], Mattias Põldaru [et], Tong Hui [zh_CN],
Victor Ibragimov [tg], Enrico Nicoletto [pt_BR], Daniel Mustieles [es],
Fran Diéguez [gl], Kjartan Maraas [nb], Nilamdyuti Goswami [as],
Aurimas Černius [lt], Stas Solovey [ru], Yosef Or Boczko [he],
Jorge Pérez Pérez [an], Dimitris Spingos [el], Baurzhan Muftakhidinov [kk],
Chao-Hsiung Liao [zh_HK, zh_TW], Shankar Prasad [kn], Yaron Shahrabani [he],
Andika Triwidada [id]
3.11.4
======
* Fix removal of workspacaes that are not at the end [Giovanni; #721417]
* Allow session mode to be specified in the environment [Ray; #720894]
* Special-case launching of terminals [Debarshi; #695010]
* Always show arrow if app switcher is scrollable [Jonh; #711467]
* Implement new app folders system [Jasper; #722117]
* Remove arrow from background menu [Tarun; #699608]
* Misc bug fixes and cleanups [Giovanni, Andika, Florian, Ray; #721039,
#721439, #721507, #721629, #721868, #722210]
Contributors:
Giovanni Campagna, Piotr Drąg, Tarun Kumar Joshi, Florian Müllner,
Debarshi Ray, Jasper St. Pierre, Ray Strode, Andika Triwidada, Jonh Wendell
Translations:
Dušan Kazik [sk], Tong Hui [zh_CN], Benjamin Steinwender [de],
Matej Urbančič [sl], Jorge Pérez Pérez [an], Kjartan Maraas [nb],
Milo Casagrande [it], Rafael Ferreira [pt_BR], Marek Černocký [cs],
Daniel Mustieles [es], Adorilson Bezerra [pt_BR], Christian Kirbach [de],
Aurimas Černius [lt], Andika Triwidada [id], Baurzhan Muftakhidinov [kk],
Victor Ibragimov [tg], Yosef Or Boczko [he], Dimitris Spingos [el],
Fran Diéguez [gl]
3.11.3
======
* Fix fade effect of desktop icons [Florian; #707671]
* Fix issues with background management code [Jasper; #709313]
* Use new Glib facilities for application search [Jasper; #711631]
* Add focus indication to session menu button [Sebastien; #710539]
* Fix hover tracking for StEntries [Jasper; #706749]
* Fix reentrancy issue in message tray [Jasper; #711694]
* Tone down zoom animation on login/unlock [Jasper; #712362]
* Allow specifying monitor for OSD [Carlos; #712664]
* Fix resetting prompt on user switch [Ray; #710456]
* Stop using gnome-bluetooth-applet [Bastien; #719341]
* Add support for EAP-FAST password requests [Dan; #719813]
* Fix entry focus of chat notifications [Jasper; #709853]
* Make window previews keyboard navigatable [Jasper; #644306]
* Fix app switcher order with dialog windows [Florian; #719824]
* Allow remote search providers without icons [Debarshi; #719965]
* Fix various alignment issues in RTL locales [Yosef; #712638, #712596,
#712594, #712600, #712579]
* Misc. bug fixes and cleanups [Jasper, Florian, Giovanni, Dan; #712727,
#712753, #719378, #719730, #719803, #710115, #720017, #719815, #719567,
#720298]
Contributors:
Giovanni Campagna, Carlos Garnacho, Sebastien Lafargue, Tim Lunn,
Florian Müllner, Bastien Nocera, Yosef Or Boczko, Debarshi Ray,
Jasper St. Pierre, Ray Strode, Dan Williams
Translations:
Kjartan Maraas [nb], Reinout van Schouwen [nl], Rafael Ferreira [pt_BR],
Mattias Põldaru [et], Emin Tufan Çetin [tr], Jiri Grönroos [fi],
Khaled Hosny [ar], Fran Diéguez [gl], Victor Ibragimov [tg],
Daniel Mustieles [es]
3.11.2
======
* Cache search result display actors [Jasper; #704912]
* Use username in userWidget if real name doesn't fit [Jasper; #706851]
* Support shell_global_reexec_self() on OpenBSD [Antoine; #709571]
* Support disabling browser plugin [Colin; #711218]
* Restore support for 'disable-restart-buttons' [Florian; #711244]
* Validate parameters of exposed DBus methods [Florian; #699752]
* Connect applications to systemd journal if available [Colin; #711626]
* Misc bug fixes and cleanups [Florian, Jasper; #711205, #698486, #711416,
#644306, #711555, #709806, #711631, #711732]
Contributors:
Cosimo Cecchi, Antoine Jacoutot, Florian Müllner, Jasper St. Pierre,
Rico Tzschichholz, Colin Walters
Translations:
Yuri Myasoedov [ru], Kjartan Maraas [nb], Efstathios Iosifidis [el],
Benjamin Steinwender [de], eternalhui [zh_CN], Shantha kumar [ta]
3.11.1
======
* power: Use UPower directly instead of gnome-settings-daemon [Bastien; #710273]
* Implement support for new GTK+ notification API [Jasper, Giovanni, Florian;
#710137, #710596]
* gdm: Don't allow user-list to fill up the entire screen [Florian; #710555]
* Don't autostart remote search providers at login [Giovanni; #708830]
* Fix spacing in end-session dialog [Sebastien; #710543]
* Prepare for js24 [Tim; #711052]
* Misc bug fixes and cleanups [Jasper, Florian, Adel, Tim, Sebastien; #710347,
#710144, #710541, #691409, #710745, #688331, #704912]
Contributors:
Giovanni Campagna, Adel Gadllah, Sebastien Lafargue, Tim Lunn,
Florian Müllner, Bastien Nocera, Jasper St. Pierre, Rico Tzschichholz
Translations:
Stas Solovey [ru], Yosef Or Boczko [he], Rafael Ferreira [pt_BR]
3.10.1
======
* Make sure lock screen is drawn once before switching user [Giovanni; #708051]
* Fix signal strength indicators in network selector [Jasper; #708442]
* Scroll search results when focusing provider icons [Jasper; #708868]
* Add separate hover/active states to page indicators [Carlos; #708852]
* Tweak appearance of user name and avatar [Yash; #702309]
* Hide "Turn On" in network menu when disabled by hardware [Giovanni; #709635]
* Cancel open keyring prompts when the screen is locked [Florian; #708910]
* Differentiate "Not Connected" and "Off" in network menu [Giovanni; #709043]
* Make network settings items point to the right device [Giovanni; #709246]
* Remove animation of window preview titles [Sebastien; #709392]
* Add 'Notifications' switch to tray menu [Florian; #707073]
* Make dropdown arrows consistent [Carlos; #709564]
* power: Use icon from primary device for status [Jasper; #709925]
* Fix XDND drags to overview [Adel; #708887]
* Fix workspace switcher disappearing with too many workspaces [Jasper; #694881]
* Handle search results with 'special:' prefix specially [Giovanni; #707055]
* gdm: Support pre-authenticated logins from oVirt [Vinzenz; #702162]
* Use ARROW role for labels representing arrows [Alejandro; #710120]
* Make selected view in app picker persistent [Florian; #710042]
* Make network selector navigable by keyboard [Alejandro; #710144]
* Misc bug fixes [Florian, Adel, Jasper, Aleksander, Giovanni, Dan, Michael,
Tim; #709034, #709263, #698486, #709286, #709248, #709543, #696564, #703265,
#709638, #709866, #709998, #710019, #710104, #710115]
Contributors:
Giovanni Campagna, Michael Catanzaro, Vinzenz Feenstra, Adel Gadllah,
Yash Girdhar, Sebastien Lafargue, Tim Lunn, Aleksander Morgado,
Florian Müllner, Alejandro Piñeiro, Carlos Soriano, Jasper St. Pierre,
Dieter Verfaillie, Dan Winship
Translations:
Inaki Larranaga Murgoitio [eu], Christian Kirbach [de], Muhammet Kara [tr],
Aurimas Černius [lt], Ryan Lortie [eo], Rūdolfs Mazurs [lv],
Dušan Kazik [sk], Fran Diéguez [gl], Enrico Nicoletto [pt_BR],
Kjartan Maraas [nb], Victor Ibragimov [tg], Matej Urbančič [sl],
A S Alam [pa], Nilamdyuti Goswami [as], Daniel Mustieles [es],
Cheng-Chia Tseng [zh_HK, zh_TW], Mattias Põldaru [et], Kenneth Nielsen [da],
Milo Casagrande [it], Marek Černocký [cs], Ihar Hrachyshka [be],
Мирослав Николић [sr, sr@latin], Arash Mousavi [fa], Yuri Myasoedov [ru],
Gil Forcada [ca], Carles Ferrando [ca@valencia], Andika Triwidada [id],
Timo Jyrinki [fi], Piotr Drąg [pl], Rafael Ferreira [pt_BR],
Gabor Kelemen [hu], Yosef Or Boczko [he], Daniel Korostil [uk],
Wouter Bolsterlee [nl], António Lima [pt]
3.10.0.1
=========
* Fix login screen [Ray; #708691]

2
README
View File

@ -8,7 +8,7 @@ For more information about GNOME Shell, including instructions on how
to build GNOME Shell from source and how to get involved with the project,
see:
http://live.gnome.org/GnomeShell
https://wiki.gnome.org/Projects/GnomeShell
Bugs should be reported at http://bugzilla.gnome.org against the 'gnome-shell'
product.

View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/sh
# Run this to generate all the initial makefiles, etc.
srcdir=`dirname $0`

View File

@ -13,9 +13,7 @@
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Jasper St. Pierre <jstpierre@mecheye.net>
@ -43,6 +41,8 @@
#define PLUGIN_API_VERSION 5
#define EXTENSION_DISABLE_VERSION_CHECK_KEY "disable-extension-version-validation"
typedef struct {
GDBusProxy *proxy;
} PluginData;
@ -833,6 +833,16 @@ plugin_get_shell_version (PluginObject *obj,
return ret;
}
static gboolean
plugin_get_version_validation_enabled (PluginObject *obj,
NPVariant *result)
{
gboolean is_enabled = !g_settings_get_boolean (obj->settings, EXTENSION_DISABLE_VERSION_CHECK_KEY);
BOOLEAN_TO_NPVARIANT(is_enabled, *result);
return TRUE;
}
#define METHODS \
METHOD (list_extensions) \
METHOD (get_info) \
@ -852,6 +862,8 @@ static NPIdentifier api_version_id;
static NPIdentifier shell_version_id;
static NPIdentifier onextension_changed_id;
static NPIdentifier onrestart_id;
static NPIdentifier version_validation_enabled_id;
static bool
plugin_object_has_method (NPObject *npobj,
@ -894,7 +906,8 @@ plugin_object_has_property (NPObject *npobj,
return (name == onextension_changed_id ||
name == onrestart_id ||
name == api_version_id ||
name == shell_version_id);
name == shell_version_id ||
name == version_validation_enabled_id);
}
static bool
@ -912,6 +925,8 @@ plugin_object_get_property (NPObject *npobj,
return plugin_get_api_version (obj, result);
else if (name == shell_version_id)
return plugin_get_shell_version (obj, result);
else if (name == version_validation_enabled_id)
return plugin_get_version_validation_enabled (obj, result);
else if (name == onextension_changed_id)
{
if (obj->listener)
@ -990,6 +1005,7 @@ init_methods_and_properties (void)
/* this is the JS public API; it is manipulated through NPIdentifiers for speed */
api_version_id = funcs.getstringidentifier ("apiVersion");
shell_version_id = funcs.getstringidentifier ("shellVersion");
version_validation_enabled_id = funcs.getstringidentifier ("versionValidationEnabled");
get_info_id = funcs.getstringidentifier ("getExtensionInfo");
list_extensions_id = funcs.getstringidentifier ("listExtensions");

View File

@ -1,5 +1,5 @@
AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.10.0.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_INIT([gnome-shell],[3.13.2],[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,6 +16,7 @@ m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
# Checks for programs.
AC_PROG_CC
AC_PROG_CXX
# Initialize libtool
LT_PREREQ([2.2.6])
@ -57,10 +58,25 @@ fi
AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
CLUTTER_MIN_VERSION=1.13.4
AC_ARG_ENABLE([systemd],
AS_HELP_STRING([--enable-systemd], [Use systemd]),
[enable_systemd=$enableval],
[enable_systemd=auto])
AS_IF([test x$enable_systemd != xno], [
AC_MSG_CHECKING([for libsystemd-journal])
PKG_CHECK_EXISTS([libsystemd-journal],
[have_systemd=yes
AC_DEFINE([HAVE_SYSTEMD], [1], [Define if we have systemd])],
[have_systemd=no])
AC_MSG_RESULT($have_systemd)
])
AC_MSG_RESULT($enable_systemd)
CLUTTER_MIN_VERSION=1.15.90
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=1.35.4
MUTTER_MIN_VERSION=3.10.0
GJS_MIN_VERSION=1.39.0
MUTTER_MIN_VERSION=3.13.2
GTK_MIN_VERSION=3.7.9
GIO_MIN_VERSION=2.37.0
LIBECAL_MIN_VERSION=3.5.3
@ -70,7 +86,6 @@ POLKIT_MIN_VERSION=0.100
STARTUP_NOTIFICATION_MIN_VERSION=0.11
GCR_MIN_VERSION=3.7.5
GNOME_DESKTOP_REQUIRED_VERSION=3.7.90
GNOME_MENUS_REQUIRED_VERSION=3.5.3
NETWORKMANAGER_MIN_VERSION=0.9.8
PULSE_MIN_VERS=2.0
@ -80,7 +95,6 @@ SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION
gtk+-3.0 >= $GTK_MIN_VERSION
atk-bridge-2.0
gjs-internals-1.0 >= $GJS_MIN_VERSION
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
$recorder_modules
gdk-x11-3.0 libsoup-2.4
xtst
@ -91,43 +105,37 @@ SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION
libcanberra libcanberra-gtk3
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
polkit-agent-1 >= $POLKIT_MIN_VERSION
libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
libsecret-unstable gcr-base-3 >= $GCR_MIN_VERSION"
gcr-base-3 >= $GCR_MIN_VERSION"
if test x$have_systemd = xyes; then
SHARED_PCS="${SHARED_PCS} libsystemd-journal"
fi
PKG_CHECK_MODULES(GNOME_SHELL, $SHARED_PCS)
PKG_CHECK_MODULES(MUTTER, libmutter >= $MUTTER_MIN_VERSION)
PKG_CHECK_MODULES(MUTTER_WAYLAND, [libmutter-wayland >= $MUTTER_MIN_VERSION],
[MUTTER_WAYLAND_TYPELIB_DIR=`$PKG_CONFIG --variable=typelibdir libmutter-wayland`
AC_SUBST(MUTTER_WAYLAND_TYPELIB_DIR)
have_mutter_wayland=yes],
[have_mutter_wayland=no])
AM_CONDITIONAL(HAVE_MUTTER_WAYLAND, test $have_mutter_wayland != no)
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 >= $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.9.0],
[BLUETOOTH_DIR=`$PKG_CONFIG --variable=applet_libdir gnome-bluetooth-1.0`
BLUETOOTH_LIBS=`$PKG_CONFIG --variable=applet_libs gnome-bluetooth-1.0`
AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])
AC_SUBST([BLUETOOTH_DIR],["$BLUETOOTH_DIR"])
AC_DEFINE_UNQUOTED([BLUETOOTH_DIR],["$BLUETOOTH_DIR"],[Path to installed GnomeBluetooth typelib and library])
AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet])
AC_SUBST([HAVE_BLUETOOTH],[1])
AC_MSG_RESULT([yes])],
AC_ARG_ENABLE(browser-plugin,
[AS_HELP_STRING([--enable-browser-plugin],
[Enable browser plugin [default=yes]])],,
enable_browser_plugin=yes)
AS_IF([test x$enable_browser_plugin = xyes], [
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
])
AM_CONDITIONAL(BUILD_BROWSER_PLUGIN, test x$enable_browser_plugin = xyes)
PKG_CHECK_MODULES(BLUETOOTH, gnome-bluetooth-1.0 >= 3.9.0,
[AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet])
AC_SUBST([HAVE_BLUETOOTH],[1])],
[AC_DEFINE([HAVE_BLUETOOTH],[0])
AC_SUBST([HAVE_BLUETOOTH],[0])
AC_MSG_RESULT([no])])
AC_SUBST([HAVE_BLUETOOTH],[0])])
PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION gio-2.0)
AC_SUBST(CALENDAR_SERVER_CFLAGS)
@ -147,6 +155,9 @@ AC_SUBST(MUTTER_TYPELIB_DIR)
GJS_CONSOLE=`$PKG_CONFIG --variable=gjs_console gjs-1.0`
AC_SUBST(GJS_CONSOLE)
GLIB_COMPILE_RESOURCES=`$PKG_CONFIG --variable glib_compile_resources gio-2.0`
AC_SUBST(GLIB_COMPILE_RESOURCES)
AC_CHECK_FUNCS(fdwalk)
AC_CHECK_FUNCS(mallinfo)
AC_CHECK_HEADERS([sys/resource.h])
@ -162,6 +173,38 @@ if test "$langinfo_ok" = "yes"; then
[Define if _NL_TIME_FIRST_WEEKDAY is available])
fi
AC_ARG_ENABLE(networkmanager,
AS_HELP_STRING([--disable-networkmanager],
[disable NetworkManager support @<:@default=auto@:>@]),,
[enable_networkmanager=auto])
if test "x$enable_networkmanager" != "xno"; then
PKG_CHECK_MODULES(NETWORKMANAGER,
[libnm-glib
libnm-util >= $NETWORKMANAGER_MIN_VERSION
libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
libsecret-unstable],
[have_networkmanager=yes],
[have_networkmanager=no])
GNOME_SHELL_CFLAGS="$GNOME_SHELL_CFLAGS $NETWORKMANAGER_CFLAGS"
GNOME_SHELL_LIBS="$GNOME_SHELL_LIBS $NETWORKMANAGER_LIBS"
else
have_networkmanager="no (disabled)"
fi
if test "x$have_networkmanager" = "xyes"; then
AC_DEFINE(HAVE_NETWORKMANAGER, [1], [Define if we have NetworkManager])
AC_SUBST([HAVE_NETWORKMANAGER], [1])
else
if test "x$enable_networkmanager" = "xyes"; then
AC_MSG_ERROR([Couldn't find NetworkManager.])
fi
AC_SUBST([HAVE_NETWORKMANAGER], [0])
fi
AM_CONDITIONAL(HAVE_NETWORKMANAGER, test "$have_networkmanager" = "yes")
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS
AM_PATH_GLIB_2_0()
@ -180,6 +223,14 @@ fi
AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no)
GNOME_COMPILE_WARNINGS([error])
case "$WARN_CFLAGS" in
*-Werror*)
WARN_CFLAGS="$WARN_CFLAGS -Wno-error=deprecated-declarations"
;;
esac
AM_CFLAGS="$AM_CFLAGS $WARN_CFLAGS"
AC_SUBST(AM_CFLAGS)
BROWSER_PLUGIN_DIR="${BROWSER_PLUGIN_DIR:-"\${libdir}/mozilla/plugins"}"
AC_ARG_VAR([BROWSER_PLUGIN_DIR],[Where to install the plugin to])
@ -203,3 +254,15 @@ AC_CONFIG_FILES([
man/Makefile
])
AC_OUTPUT
echo "
Build configuration:
Prefix: ${prefix}
Source code location: ${srcdir}
Compiler: ${CC}
Compiler Warnings: $enable_compile_warnings
Support for NetworkManager: $have_networkmanager
Support for GStreamer recording: $build_recorder
"

View File

@ -1,12 +1,5 @@
wandadir = $(pkgdatadir)
dist_wanda_DATA = wanda.png
desktopdir=$(datadir)/applications
desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
if HAVE_MUTTER_WAYLAND
desktop_DATA += gnome-shell-wayland.desktop
endif HAVE_MUTTER_WAYLAND
desktop_DATA = gnome-shell.desktop gnome-shell-wayland.desktop gnome-shell-extension-prefs.desktop
# We substitute in bindir so it works as an autostart
# file when built in a non-system prefix
@ -42,11 +35,14 @@ dist_theme_DATA = \
theme/filter-selected-rtl.svg \
theme/gnome-shell.css \
theme/logged-in-indicator.svg \
theme/menu-arrow-symbolic.svg \
theme/message-tray-background.png \
theme/more-results.svg \
theme/noise-texture.png \
theme/page-indicator-active.svg \
theme/page-indicator-active.svg \
theme/page-indicator-inactive.svg \
theme/page-indicator-checked.svg \
theme/page-indicator-hover.svg \
theme/panel-button-border.svg \
theme/panel-button-highlight-narrow.svg \
theme/panel-button-highlight-wide.svg \

View File

@ -2,7 +2,7 @@
Type=Application
_Name=GNOME Shell (wayland compositor)
_Comment=Window management and application launching
Exec=@bindir@/mutter-launch -- gnome-shell-wayland --wayland
Exec=@bindir@/gnome-shell --wayland --display-server
X-GNOME-Bugzilla-Bugzilla=GNOME
X-GNOME-Bugzilla-Product=gnome-shell
X-GNOME-Bugzilla-Component=general

View File

@ -13,29 +13,37 @@
</key>
<key name="enabled-extensions" type="as">
<default>[]</default>
<_summary>Uuids of extensions to enable</_summary>
<_summary>UUIDs of extensions to enable</_summary>
<_description>
GNOME Shell extensions have a uuid property; this key lists extensions
GNOME Shell extensions have a UUID property; this key lists extensions
which should be loaded. Any extension that wants to be loaded needs
to be in this list. You can also manipulate this list with the
EnableExtension and DisableExtension DBus methods on org.gnome.Shell.
EnableExtension and DisableExtension D-Bus methods on org.gnome.Shell.
</_description>
</key>
<key name="disable-extension-version-validation" type="b">
<default>false</default>
<_summary>Disables the validation of extension version compatibility</_summary>
<_description>
GNOME Shell will only load extensions that claim to support the current
running version. Enabling this option will disable this check and try to
load all extensions regardless of the versions they claim to support.
</_description>
</key>
<key name="favorite-apps" type="as">
<default>[ 'epiphany.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'libreoffice-writer.desktop', 'nautilus.desktop', 'gnome-documents.desktop' ]</default>
<default>[ 'epiphany.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'libreoffice-writer.desktop', 'org.gnome.Nautilus.desktop', 'org.gnome.Documents.desktop' ]</default>
<_summary>List of desktop file IDs for favorite applications</_summary>
<_description>
The applications corresponding to these identifiers
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 name="app-picker-view" type="u">
<default>0</default>
<summary>App Picker View</summary>
<description>
Index of the currently selected view in the application picker.
</description>
</key>
<key name="command-history" type="as">
<default>[]</default>
@ -47,10 +55,10 @@
</key>
<key name="always-show-log-out" type="b">
<default>false</default>
<_summary>Always show the 'Log out' menuitem in the user menu.</_summary>
<_summary>Always show the 'Log out' menu item in the user menu.</_summary>
<_description>
This key overrides the automatic hiding of the 'Log out'
menuitem in single-user, single-session situations.
menu item in single-user, single-session situations.
</_description>
</key>
<key name="remember-mount-password" type="b">
@ -66,6 +74,7 @@
<child name="calendar" schema="org.gnome.shell.calendar"/>
<child name="keybindings" schema="org.gnome.shell.keybindings"/>
<child name="keyboard" schema="org.gnome.shell.keyboard"/>
<child name="location" schema="org.gnome.shell.location"/>
</schema>
<schema id="org.gnome.shell.calendar" path="/org/gnome/shell/calendar/"
@ -117,6 +126,11 @@
Keybinding to focus the active notification.
</_description>
</key>
<key name="pause-resume-tweens" type="as">
<default>[]</default>
<summary>Keybinding that pauses and resumes all running tweens, for debugging purposes</summary>
<description></description>
</key>
</schema>
<schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/"
@ -130,6 +144,32 @@
</key>
</schema>
<enum id="org.gnome.shell.geoclue.AccuracyLevel">
<value value="0" nick="off"/>
<value value="1" nick="country"/>
<value value="4" nick="city"/>
<value value="5" nick="neighborhood"/>
<value value="6" nick="street"/>
<value value="8" nick="exact"/>
</enum>
<schema id="org.gnome.shell.location"
path="/org/gnome/shell/location/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="max-accuracy-level" enum="org.gnome.shell.geoclue.AccuracyLevel">
<default>'exact'</default>
<_summary>The maximum accuracy level of location.</_summary>
<_description>
Configures the maximum level of location accuracy applications are
allowed to see. Valid options are 'off' (disable location tracking),
'country', 'city', 'neighborhood', 'street', and 'exact' (typically
requires GPS receiver). Please keep in mind that this only controls
what GeoClue will allow applications to see and they can find user's
location on their own using network resources (albeit with street-level
accuracy at best).
</_description>
</key>
</schema>
<schema id="org.gnome.shell.app-switcher"
path="/org/gnome/shell/app-switcher/"
gettext-domain="@GETTEXT_PACKAGE@">
@ -181,15 +221,6 @@
</_description>
</key>
<key name="button-layout" type="s">
<default>":close"</default>
<_summary>Arrangement of buttons on the titlebar</_summary>
<_description>
This key overrides the key in org.gnome.desktop.wm.preferences when
running GNOME Shell.
</_description>
</key>
<key name="edge-tiling" type="b">
<default>true</default>
<_summary>Enable edge tiling when dropping windows on screen edges</_summary>

View File

@ -153,18 +153,43 @@ StScrollBar StButton#vhandle:active {
-arrow-rise: 11px;
}
.popup-menu-boxpointer.fallback-app-menu {
-arrow-border-radius: 4px;
-arrow-background-color: #ededed;
-arrow-border-width: 1px;
-arrow-border-color: #a6a6a6;
-arrow-base: 24px;
-arrow-rise: 11px;
}
.popup-menu-boxpointer.fallback-app-menu.dark {
-arrow-background-color: #3f4747;
-arrow-border-color: #282b2b;
}
.popup-menu {
min-width: 200px;
}
.popup-submenu-menu-item-triangle {
font-size: 120%;
.popup-menu-arrow {
width: 16px;
height: 16px;
}
.popup-submenu-menu-item:open {
background-color: #333333;
}
.fallback-app-menu .popup-submenu-menu-item:open {
background-color: #888888;
color: white;
}
.fallback-app-menu.dark .popup-submenu-menu-item:open {
background-color: #333333;
}
.popup-sub-menu {
background-gradient-start: rgba(80,80,80,0.3);
background-gradient-end: rgba(80,80,80,0.4);
@ -172,6 +197,20 @@ StScrollBar StButton#vhandle:active {
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.9);
}
.fallback-app-menu .popup-sub-menu {
background-gradient-start: #dddddd;
background-gradient-end: #dfdfdf;
background-gradient-direction: vertical;
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.4);
}
.fallback-app-menu.dark .popup-sub-menu {
background-gradient-start: #474747;
background-gradient-end: #4b4b4b;
background-gradient-direction: vertical;
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.9);
}
.popup-sub-menu:scrolled .popup-menu-item:ltr {
padding-right: 0em;
}
@ -220,7 +259,29 @@ StScrollBar StButton#vhandle:active {
color: #9f9f9f;
}
.popup-image-menu-item {
.fallback-app-menu .popup-menu-item {
color: #43484a;
}
.fallback-app-menu .popup-menu-item:active {
color: white;
background-color: #4689cd;
}
.fallback-app-menu .popup-menu-item:insensitive {
color: gray;
}
.fallback-app-menu.dark .popup-menu-item {
color: white;
}
.fallback-app-menu.dark .popup-menu-item:active {
background-color: #445f7d;
}
.fallback-app-menu.dark .popup-menu-item:insensitive {
color: #9f9f9f;
}
.popup-separator-menu-item {
@ -232,8 +293,15 @@ StScrollBar StButton#vhandle:active {
padding: 8px 0px;
}
.popup-alternating-menu-item:alternate {
font-weight: bold;
.fallback-app-menu .popup-separator-menu-item {
-gradient-start: #a0a2a3;
-gradient-end: #a0a2a3;
-margin-horizontal: 0;
}
.fallback-app-menu.dark .popup-separator-menu-item {
-gradient-start: #818584;
-gradient-end: #818584;
}
.popup-status-menu-item {
@ -289,6 +357,20 @@ StScrollBar StButton#vhandle:active {
spacing: 10px;
}
.nm-dialog-airplane-box {
spacing: 12px;
}
.nm-dialog-airplane-headline {
font-size: 1.1em;
font-weight: bold;
text-align: center;
}
.nm-dialog-airplane-text {
color: #999999;
}
.nm-dialog-header-icon {
icon-size: 32px;
}
@ -306,16 +388,13 @@ StScrollBar StButton#vhandle:active {
font-size: 12pt;
border-bottom: 1px solid #666;
padding: 12px;
}
.nm-dialog-item:checked {
background-color: #333;
}
.nm-dialog-item-box {
spacing: 20px;
}
.nm-dialog-item:selected {
background-color: #333;
}
.nm-dialog-icons {
spacing: .5em;
}
@ -324,13 +403,18 @@ StScrollBar StButton#vhandle:active {
icon-size: 16px;
}
.no-networks-label {
color: #999999;
}
.no-networks-box {
spacing: 12px;
}
/* Buttons */
.candidate-page-button,
.notification-button,
.notification-icon-button,
.hotplug-notification-item,
.hotplug-resident-eject-button,
.modal-dialog-button,
.app-view-control {
border: 1px solid #8b8b8b;
@ -344,17 +428,12 @@ StScrollBar StButton#vhandle:active {
}
.candidate-page-button:hover,
.notification-button:hover,
.notification-icon-button:hover,
.hotplug-notification-item:hover,
.hotplug-resident-eject-button:hover,
.modal-dialog-button:hover {
background-gradient-start: rgba(255, 255, 255, 0.3);
background-gradient-end: rgba(255, 255, 255, 0.1);
}
.notification-button:focus,
.notification-icon-button:focus,
.hotplug-notification-item:focus,
.modal-dialog-button:focus,
.app-view-control:focus {
@ -368,10 +447,7 @@ StScrollBar StButton#vhandle:active {
.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,
.app-view-control:checked {
@ -380,8 +456,6 @@ StScrollBar StButton#vhandle:active {
}
.candidate-page-button:insensitive,
.notification-button:insensitive,
.notification-icon-button:insensitive,
.modal-dialog-button:insensitive {
border-color: #666666;
color: #9f9f9f;
@ -393,7 +467,6 @@ StScrollBar StButton#vhandle:active {
#searchEntry,
.modal-dialog-button,
.notification-button,
.hotplug-notification-item,
.app-view-controls,
#screenShieldNotifications {
@ -570,11 +643,6 @@ StScrollBar StButton#vhandle:active {
app-icon-bottom-clip: 2px;
}
.app-menu-icon {
width: 24px;
height: 24px;
}
.panel-button {
-natural-hpadding: 12px;
-minimum-hpadding: 6px;
@ -658,7 +726,7 @@ StScrollBar StButton#vhandle:active {
color: #e6e6e6;
border-radius: 32px; /* wish we could do 50% */
padding: 13px;
border: 1px solid #5f5f5f; /* using rgba() is flaky unfortunately */
border: 2px solid #5f5f5f; /* using rgba() is flaky unfortunately */
}
.system-menu-action:hover,
@ -666,7 +734,7 @@ StScrollBar StButton#vhandle:active {
color: white;
background-color: #4c4c4c;
border: none;
padding: 14px;
padding: 15px;
}
.system-menu-action:active {
@ -693,7 +761,9 @@ StScrollBar StButton#vhandle:active {
padding-bottom: 32px;
}
.workspace-thumbnails-background {
.workspace-thumbnails {
spacing: 11px;
visible-width: 32px; /* Amount visible before hovering */
border: 1px solid rgba(128, 128, 128, 0.4);
border-right: 0px;
border-radius: 9px 0px 0px 9px;
@ -701,18 +771,13 @@ StScrollBar StButton#vhandle:active {
padding: 11px 7px 11px 11px;
}
.workspace-thumbnails-background:rtl {
.workspace-thumbnails:rtl {
border-right: 1px;
border-left: 0px;
border-radius: 0px 9px 9px 0px;
padding: 11px 11px 11px 7px;
}
.workspace-thumbnails {
spacing: 11px;
visible-width: 32px; /* Amount visible before hovering */
}
.workspace-thumbnail-indicator {
border: 4px solid rgba(255,255,255,0.7);
border-radius: 4px;
@ -950,11 +1015,19 @@ StScrollBar StButton#vhandle:active {
background-image: url(page-indicator-inactive.svg);
}
.page-indicator:hover .page-indicator-icon,
.page-indicator:checked .page-indicator-icon {
.page-indicator:hover .page-indicator-icon {
background-image: url(page-indicator-hover.svg);
}
.page-indicator:active .page-indicator-icon {
background-image: url(page-indicator-active.svg);
}
.page-indicator:checked .page-indicator-icon,
.page-indicator:checked:active .page-indicator-icon {
background-image: url(page-indicator-checked.svg);
}
.no-frequent-applications-label {
font-size: 18pt;
color: #999999;
@ -962,6 +1035,8 @@ StScrollBar StButton#vhandle:active {
.app-folder-icon {
padding: 5px;
spacing-rows: 5px;
spacing-columns: 5px;
}
.dash-item-container > StButton {
@ -1138,11 +1213,6 @@ StScrollBar StButton#vhandle:active {
text-shadow: black 0px 2px 2px;
}
#LookingGlassDialog .lg-inspector-title {
font-weight: bold;
padding-bottom: 8px;
}
.lg-dialog StEntry {
selection-background-color: #bbbbbb;
selected-color: #333333;
@ -1222,14 +1292,8 @@ StScrollBar StButton#vhandle:active {
width: 0.3em;
}
#calendarPopup .calendar {
padding: 10px;
}
.calendar {
padding: .4em 1.75em .8em 1.75em;
spacing-rows: 0px;
spacing-columns: 0px;
}
.calendar-month-label {
@ -1287,12 +1351,18 @@ StScrollBar StButton#vhandle:active {
font-weight: bold;
text-align: center;
color: #eeeeec;
border-radius: 4px;
}
.datemenu-date-label:hover,
.datemenu-date-label:focus {
background-color: #999999;
}
.datemenu-date-label:active {
background-color: #aaaaaa;
}
.calendar-day-base {
font-size: 9pt;
text-align: center;
@ -1419,107 +1489,87 @@ StScrollBar StButton#vhandle:active {
padding-right: 8pt;
}
/* Message Tray */
.notification-drawer {
background: rgba(0,0,0,0.8);
padding: 1em;
border: 2px solid #4c4c4c;
border-bottom: 0px;
border-radius: 6px 6px 0 0;
}
.system-tray-icons,
.notification-drawer-footer-actions {
spacing: 0.5em;
}
.system-tray-icon-button,
.notification-drawer-button {
border-radius: 4px;
border: 1px solid #4c4c4c;
padding: 4px;
}
.system-tray-icon-button {
width: 22px;
height: 22px;
}
.notification-drawer-button StIcon {
icon-size: 22px;
}
.system-tray-icon-button:hover,
.notification-drawer-button:hover {
background: 1px solid #4c4c4c;
}
.url-highlighter {
link-color: #ccccff;
}
/* Message Tray */
#message-tray {
background: #2e3436 url(message-tray-background.png);
background-repeat: repeat;
height: 72px;
}
.message-tray-summary {
height: 72px;
}
.message-tray-menu-button StIcon {
padding: 0 20px;
color: #aaaaaa;
icon-size: 24px;
}
.message-tray-menu-button:hover StIcon,
.message-tray-menu-button:active StIcon,
.message-tray-menu-button:focus StIcon {
color: #eeeeee;
}
.no-messages-label,
.no-networks-label {
color: #999999;
}
.notification {
border-radius: 10px 10px 0px 0px;
background: rgba(0,0,0,0.9);
padding: 8px 8px 4px 8px;
spacing-rows: 4px;
spacing-columns: 10px;
}
.notification, #notification-container {
.notification, #notification-container, .notification-drawer {
font-size: 11pt;
width: 34em;
}
.notification.multi-line-notification {
padding-bottom: 8px;
.notification-main-content {
padding: 8px;
spacing: 8px;
border-radius: 10px 10px 0px 0px;
background: rgba(0,0,0,0.8);
}
.notification-unexpanded {
/* We want to force the actor at a specific size, irrespective
of its minimum and preferred size, so we override both */
min-height: 36px;
height: 36px;
.notification-action-area {
padding: 8px;
}
/* We use row-span = 2 for the image cell, which prevents its height preferences to be
taken into account during allocation, so its height ends up being limited by the height
of the content in the other rows. To avoid showing a stretched image, we set the minimum
height of the table to be ICON_SIZE + IMAGE_SIZE + spacing-rows = 24 + 125 + 10 = 159 */
.notification-with-image {
min-height: 159px;
.notification-action-area,
.notification-button {
background: rgba(0,0,0,0.8);
border-top: 1px solid #666;
}
.summary-boxpointer {
-arrow-border-radius: 15px;
-arrow-background-color: rgba(0,0,0,0.9);
-arrow-base: 36px;
-arrow-rise: 18px;
color: white;
-boxpointer-gap: 4px;
.notification-button {
padding: 8px 0px;
border-right: 1px solid #666;
}
.summary-boxpointer .notification {
border-radius: 9px;
background: rgba(0,0,0,0) !important;
padding-bottom: 12px;
.notification-button:hover {
background: rgba(255,255,255,0.3);
}
.summary-boxpointer #summary-right-click-menu {
padding-top: 12px;
padding-bottom: 12px;
.notification-button:active {
background: rgba(255,255,255,0.1);
}
.summary-notification-stack-scrollview {
max-height: 18em;
padding-top: 8px;
padding-bottom: 8px;
.notification-button:last-child {
border-right-width: 0px;
}
.summary-notification-stack-scrollview:ltr {
padding-right: 8px;
}
.summary-notification-stack-scrollview:rtl {
padding-left: 8px;
}
.notification-scrollview {
max-height: 10em;
-st-vfade-offset: 24px;
.notification-title-box {
spacing: 8px;
}
.notification-scrollview:ltr > StScrollBar {
@ -1530,37 +1580,9 @@ StScrollBar StButton#vhandle:active {
padding-right: 6px;
}
.notification-body {
spacing: 5px;
}
.notification-actions {
padding-top: 18px;
spacing: 10px;
}
.notification-button {
-st-natural-width: 140px;
padding: 4px 4px 5px;
}
.notification-button:focus {
-st-natural-width: 138px;
padding: 3px 4px 4px;
}
.notification-icon-button {
border-radius: 5px;
padding: 5px;
}
.notification-icon-button:focus {
padding: 4px;
}
.notification-icon-button > StIcon {
icon-size: 16px;
padding: 8px;
.notification-scrollview {
max-height: 10em;
-st-vfade-offset: 24px;
}
.secondary-icon {
@ -1585,51 +1607,12 @@ StScrollBar StButton#vhandle:active {
padding: 2px 5px;
}
.hotplug-resident-box {
spacing: 8px;
}
.hotplug-resident-mount {
spacing: 8px;
border-radius: 4px;
color: #ccc;
}
.hotplug-resident-mount:hover {
background-gradient-direction: horizontal;
background-gradient-start: rgba(255, 255, 255, 0.1);
background-gradient-end: rgba(255, 255, 255, 0);
color: #fff;
}
.hotplug-resident-mount-label {
color: inherit;
padding-left: 6px;
}
.hotplug-resident-mount-icon {
icon-size: 24px;
padding-left: 6px;
}
.hotplug-resident-eject-icon {
icon-size: 16px;
}
.hotplug-resident-eject-button {
padding: 7px;
border-radius: 5px;
color: #ccc;
}
.chat-log-message {
color: #888888;
}
.chat-group-sent, .chat-group-meta {
padding: 8px 0;
.chat-empty-line {
font-size: 4px;
}
.chat-received {
@ -1654,6 +1637,7 @@ StScrollBar StButton#vhandle:active {
.chat-meta-message {
padding-left: 4px;
font-size: 9pt;
font-weight: bold;
color: #bbbbbb;
}
@ -1662,7 +1646,11 @@ StScrollBar StButton#vhandle:active {
padding-right: 4px;
}
.chat-notification-scrollview{
.chat-notification-body-box {
spacing: 5px;
}
.chat-notification-scrollview {
max-height: 22em;
}
@ -1712,6 +1700,28 @@ StScrollBar StButton#vhandle:active {
-shell-counter-overlap-y: 13px;
}
.message-tray-indicator {
spacing: 4px;
}
.message-tray-indicator-count {
font-size: 1.2em;
font-weight: bold;
color: black;
background-color: rgba(255, 255, 255, 0.7);
border-radius: 1em;
padding: 1em;
text-align: center;
}
.message-tray-indicator-glow {
height: 4px;
background-gradient-start: rgba(255, 255, 255, 0);
background-gradient-end: rgba(255, 255, 255, 1);
background-gradient-direction: vertical;
}
/* OSD */
.osd-window {
text-align: center;
@ -1743,26 +1753,6 @@ StScrollBar StButton#vhandle:active {
spacing: 8px;
}
.thumbnail-scroll-gradient-left {
background-gradient-direction: horizontal;
background-gradient-start: rgba(51, 51, 51, 1.0);
background-gradient-end: rgba(51, 51, 51, 0);
border-radius: 24px;
border-radius-topright: 0px;
border-radius-bottomright: 0px;
width: 60px;
}
.thumbnail-scroll-gradient-right {
background-gradient-direction: horizontal;
background-gradient-start: rgba(51, 51, 51, 0);
background-gradient-end: rgba(51, 51, 51, 1.0);
border-radius: 24px;
border-radius-topleft: 0px;
border-radius-bottomleft: 0px;
width: 60px;
}
.switcher-list .item-box {
padding: 8px;
border-radius: 8px;
@ -1855,6 +1845,27 @@ StScrollBar StButton#vhandle:active {
border-radius: 8px;
}
/* Tile previews */
.tile-preview {
background-color: rgba(74, 144, 217, 0.35);
border: 1px solid #4a90d9; /* Adwaita selected bg color */
}
.tile-preview-left.on-primary {
/* keep in sync with -panel-corner-radius */
border-radius: 6px 0 0 0;
}
.tile-preview-right.on-primary {
/* keep in sync with -panel-corner-radius */
border-radius: 0 6px 0 0;
}
.tile-preview-left.tile-preview-right.on-primary {
/* keep in sync with -panel-corner-radius */
border-radius: 6px 6px 0 0;
}
/* Modal Dialogs */
/* Dialog Subject Text Style */
@ -1925,50 +1936,72 @@ StScrollBar StButton#vhandle:active {
padding-top: 20px;
}
.end-session-dialog-subject {
.end-session-dialog-layout {
padding-left: 17px;
padding-bottom: 20px;
}
.end-session-dialog-subject:rtl {
padding-left: 0px;
.end-session-dialog-layout:rtl {
padding-right: 17px;
}
.end-session-dialog-description {
padding-left: 17px;
width: 28em;
padding-bottom: 10px;
}
.end-session-dialog-description:rtl {
padding-right: 17px;
width: 28em;
padding-bottom: 10px;
text-align: right;
}
.end-session-dialog-warning {
width: 28em;
color: #f57900;
padding-top: 6px;
}
.end-session-dialog-warning:rtl {
width: 28em;
color: #f57900;
padding-top: 6px;
text-align: right;
}
.end-session-dialog-logout-icon {
border: 2px solid #8b8b8b;
border-radius: 5px;
width: 32px;
height: 32px;
width: 48px;
height: 48px;
background-size: contain;
}
.end-session-dialog-shutdown-icon {
color: #bebebe;
width: 32px;
height: 32px;
width: 48px;
height: 48px;
}
.end-session-dialog-inhibitor-layout {
spacing: 16px;
max-height: 200px;
padding-right: 50px;
padding-left: 50px;
padding-right: 65px;
padding-left: 65px;
}
.end-session-dialog-session-list,
.end-session-dialog-app-list {
spacing: 1em;
}
.end-session-dialog-list-header {
font-weight: bold;
}
.end-session-dialog-list-header:rtl {
text-align: right;
}
.end-session-dialog-app-list-item,
.end-session-dialog-session-list-item {
spacing: 1em;
@ -1989,10 +2022,6 @@ StScrollBar StButton#vhandle:active {
icon-size: 48px;
}
.mount-password-reask {
color: red;
}
.show-processes-dialog,
.mount-question-dialog {
spacing: 24px;
@ -2088,6 +2117,10 @@ StScrollBar StButton#vhandle:active {
color: #666666;
}
.prompt-dialog-description:rtl {
text-align: right;
}
.prompt-dialog-password-box {
spacing: 1em;
padding-bottom: 1em;
@ -2268,7 +2301,7 @@ StScrollBar StButton#vhandle:active {
.framed-user-icon {
border: 2px solid #8b8b8b;
border-radius: 5px;
border-radius: 3px;
background-size: contain;
}
@ -2284,13 +2317,6 @@ StScrollBar StButton#vhandle:active {
padding-bottom: 1em;
}
.login-dialog-title {
font-size: 14pt;
font-weight: bold;
color: #666666;
padding-bottom: 2em;
}
.login-dialog {
/* Reset border and background */
border: none;
@ -2312,8 +2338,10 @@ StScrollBar StButton#vhandle:active {
}
.login-dialog-user-list-item {
border-radius: 10px;
border-radius: 5px;
padding: .2em;
color: #bfbfbf;
text-shadow: black 0px 2px 2px;
}
.login-dialog-user-list-item:ltr {
@ -2324,23 +2352,6 @@ StScrollBar StButton#vhandle:active {
padding-left: 1em;
}
.login-dialog-user-list-item .login-dialog-user-list-item-name {
font-size: 20pt;
padding-left: 9px;
}
.login-dialog-user-list:expanded .login-dialog-user-list-item {
color: #666666;
}
.login-dialog-user-list-item,
.login-dialog-user-list-item:hover .login-dialog-user-list-item-name,
.login-dialog-user-list:expanded .login-dialog-user-list-item:focus .login-dialog-user-list-item-name,
.login-dialog-user-list:expanded .login-dialog-user-list-item:logged-in {
color: white;
text-shadow: black 0px 2px 2px;
}
.login-dialog-user-list-item:hover {
background-color: rgba(255,255,255,0.1);
}
@ -2354,10 +2365,6 @@ StScrollBar StButton#vhandle:active {
background-size: contain;
}
.login-dialog-user-list-item-text-box {
padding: 0 0.5em;
}
.login-dialog-user-list-item .login-dialog-timed-login-indicator {
background-color: rgba(0,0,0,0.0);
height: 2px;
@ -2367,13 +2374,6 @@ StScrollBar StButton#vhandle:active {
background-color: #8b8b8b;
}
.login-dialog-user-list-item-icon {
border: 2px solid #8b8b8b;
border-radius: 8px;
width: 64px;
height: 64px;
}
.login-dialog-not-listed-label {
font-size: 10.5pt;
font-weight: bold;
@ -2381,6 +2381,10 @@ StScrollBar StButton#vhandle:active {
padding-top: 1em;
}
.login-dialog-user-selection-box {
padding: 100px 0;
}
.login-dialog-user-selection-box .login-dialog-not-listed-label {
padding-left: 2px;
}
@ -2390,12 +2394,13 @@ StScrollBar StButton#vhandle:active {
color: #E8E8E8;
}
.login-dialog-username {
.login-dialog-username,
.user-widget-label {
font-size: 16pt;
font-weight: bold;
text-align: left;
padding-left: 15px;
text-shadow: black 0px 4px 3px 0px;
text-shadow: rgba(0, 0, 0, 0.5) 0px 2px 1px 0px;
}
.login-dialog-prompt-layout {
@ -2420,6 +2425,7 @@ StScrollBar StButton#vhandle:active {
}
.login-dialog-session-list-button:hover,
.login-dialog-session-list-button:focus,
.login-dialog-session-list-button:active {
color: white;
}
@ -2486,11 +2492,14 @@ StScrollBar StButton#vhandle:active {
}
.user-widget-label {
font-size: 16pt;
font-weight: bold;
text-align: left;
padding-left: 15px;
text-shadow: black 0px 4px 3px 0px;
}
.user-widget-label:ltr {
padding-left: 18px;
}
.user-widget-label:rtl {
padding-right: 18px;
}
/* Screen shield */
@ -2582,8 +2591,7 @@ StScrollBar StButton#vhandle:active {
padding-bottom: 0px;
}
#screenShieldNotifications .notification-button,
#screenShieldNotifications .notification-icon-button {
#screenShieldNotifications .notification-button {
border: 1px rgba(255,255,255,0.5);
}
@ -2617,4 +2625,5 @@ StScrollBar StButton#vhandle:active {
.background-menu {
-boxpointer-gap: 4px;
-arrow-rise: 0px;
}

View File

@ -0,0 +1,90 @@
<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
id="svg3863"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="menu-arrow.svg">
<defs
id="defs3865" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="15.836083"
inkscape:cx="-3.1641676"
inkscape:cy="11.823817"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
showguides="true"
inkscape:guide-bbox="true"
inkscape:window-width="1366"
inkscape:window-height="702"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:snap-bbox="true">
<sodipodi:guide
orientation="1,0"
position="15.996443,16.922964"
id="guide3873" />
<sodipodi:guide
orientation="0,1"
position="28.041217,3.1256134"
id="guide3875" />
<sodipodi:guide
orientation="0,1"
position="-0.80372916,24.469088"
id="guide3877" />
<sodipodi:guide
orientation="1,0"
position="3.0363102,34.649657"
id="guide3879" />
<sodipodi:guide
orientation="1,0"
position="29.023553,28.577037"
id="guide3881" />
<inkscape:grid
type="xygrid"
id="grid2988" />
</sodipodi:namedview>
<metadata
id="metadata3868">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0,-16)">
<path
style="fill:#ffffff;fill-opacity:1;stroke:none"
d="m 4,23 8,0 -4,5 z"
id="path3883"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -14,7 +14,7 @@
id="svg4703"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="page-indicator-active.svg">
sodipodi:docname="page-indicator-pushed.svg">
<defs
id="defs4705" />
<sodipodi:namedview
@ -24,18 +24,22 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="22.197802"
inkscape:cx="2.1522887"
inkscape:cy="16.782904"
inkscape:zoom="31.392433"
inkscape:cx="1.0245308"
inkscape:cy="13.3715"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="1920"
inkscape:window-height="1021"
inkscape:window-width="2560"
inkscape:window-height="1374"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1" />
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid6140" />
</sodipodi:namedview>
<metadata
id="metadata4708">
<rdf:RDF>
@ -44,7 +48,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
@ -54,14 +58,14 @@
inkscape:groupmode="layer"
transform="translate(0,2)">
<path
transform="matrix(0.72823872,0,0,0.8336417,-1512.2872,-525.55618)"
d="m 2099.9808,638.83099 c 0,5.29998 -4.9184,9.59645 -10.9854,9.59645 -6.0671,0 -10.9854,-4.29647 -10.9854,-9.59645 0,-5.29997 4.9183,-9.59645 10.9854,-9.59645 6.067,0 10.9854,4.29648 10.9854,9.59645 z"
transform="matrix(0.54617904,0,0,0.62523128,-1131.9904,-392.39214)"
d="m 2099.9808,638.83099 a 10.985409,9.5964489 0 1 1 -21.9708,0 10.985409,9.5964489 0 1 1 21.9708,0 z"
sodipodi:ry="9.5964489"
sodipodi:rx="10.985409"
sodipodi:cy="638.83099"
sodipodi:cx="2088.9954"
id="path4711"
style="fill:#fdffff;fill-opacity:0.94117647;stroke:none"
style="fill:#fdffff;fill-opacity:1;stroke:none"
sodipodi:type="arc" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,67 @@
<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="18"
height="18"
id="svg4703"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="page-indicator-active.svg">
<defs
id="defs4705" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="22.197802"
inkscape:cx="2.1522887"
inkscape:cy="16.782904"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="1920"
inkscape:window-height="1021"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1" />
<metadata
id="metadata4708">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0,2)">
<path
transform="matrix(0.72823872,0,0,0.8336417,-1512.2872,-525.55618)"
d="m 2099.9808,638.83099 c 0,5.29998 -4.9184,9.59645 -10.9854,9.59645 -6.0671,0 -10.9854,-4.29647 -10.9854,-9.59645 0,-5.29997 4.9183,-9.59645 10.9854,-9.59645 6.067,0 10.9854,4.29648 10.9854,9.59645 z"
sodipodi:ry="9.5964489"
sodipodi:rx="10.985409"
sodipodi:cy="638.83099"
sodipodi:cx="2088.9954"
id="path4711"
style="fill:#fdffff;fill-opacity:0.94117647;stroke:none"
sodipodi:type="arc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,67 @@
<?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:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="18"
height="18"
id="svg5266"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="page-indicator-inactive.svg">
<defs
id="defs5268" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="11.313709"
inkscape:cx="-2.307566"
inkscape:cy="17.859535"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="2560"
inkscape:window-height="1374"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1" />
<metadata
id="metadata5271">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0,2)">
<path
sodipodi:type="arc"
style="fill:none;fill-opacity:0;stroke:#ffffff;stroke-width:2.93356276000000005;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
id="path5274"
sodipodi:cx="2088.9954"
sodipodi:cy="638.83099"
sodipodi:rx="10.985409"
sodipodi:ry="9.5964489"
d="m 2099.9808,638.83099 c 0,5.29998 -4.9184,9.59645 -10.9854,9.59645 -6.0671,0 -10.9854,-4.29647 -10.9854,-9.59645 0,-5.29997 4.9183,-9.59645 10.9854,-9.59645 6.067,0 10.9854,4.29648 10.9854,9.59645 z"
transform="matrix(0.63720887,0,0,0.72943648,-1322.1264,-458.98661)" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -25,14 +25,14 @@
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="11.313709"
inkscape:cx="13.381365"
inkscape:cx="-2.307566"
inkscape:cy="17.859535"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:grid-bbox="true"
inkscape:document-units="px"
inkscape:window-width="1920"
inkscape:window-height="1021"
inkscape:window-width="2560"
inkscape:window-height="1374"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1" />
@ -55,7 +55,7 @@
transform="translate(0,2)">
<path
sodipodi:type="arc"
style="fill:#ffffff;fill-opacity:0;stroke:#ffffff;stroke-width:2.93356276;stroke-miterlimit:4;stroke-opacity:0.39215686;stroke-dasharray:none"
style="fill:none;fill-opacity:0;stroke:#ffffff;stroke-width:2.93356276000000005;stroke-miterlimit:4;stroke-opacity:0.39215686000000000;stroke-dasharray:none"
id="path5274"
sodipodi:cx="2088.9954"
sodipodi:cy="638.83099"

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

View File

@ -66,6 +66,7 @@ IGNORE_HFILES= \
gactionmuxer.h \
gactionobservable.h \
gactionobserver.h \
shell-network-agent.h \
shell-recorder-src.h
if !BUILD_RECORDER
@ -112,7 +113,7 @@ expand_content_files=
# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
GTKDOC_CFLAGS=$(GNOME_SHELL_CFLAGS)
GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(BLUETOOTH_LIBS) $(top_builddir)/src/libgnome-shell-menu.la $(top_builddir)/src/libgnome-shell-base.la $(top_builddir)/src/libgnome-shell.la
GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(top_builddir)/src/libgnome-shell-menu.la $(top_builddir)/src/libgnome-shell-base.la $(top_builddir)/src/libgnome-shell.la
# This includes the standard gtk-doc make rules, copied by gtkdocize.
include $(top_srcdir)/gtk-doc.make

View File

@ -50,7 +50,6 @@
<xi:include href="xml/shell-wm.xml"/>
<xi:include href="xml/shell-util.xml"/>
<xi:include href="xml/shell-mount-operation.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"/>
</chapter>

View File

@ -17,17 +17,15 @@ packages. If you are interested in building GNOME Shell from source,
we would recommend building from version control using the build
script described at:
http://live.gnome.org/GnomeShell
https://wiki.gnome.org/Projects/GnomeShell
Not only will that give you the very latest version of this rapidly
changing project, it will be much easier than get GNOME Shell and
its dependencies to build from tarballs.</description>
<!--
<homepage rdf:resource="http://live.gnome.org/GnomeShell" />
-->
<homepage rdf:resource="https://wiki.gnome.org/Projects/GnomeShell" />
<mailing-list rdf:resource="http://mail.gnome.org/mailman/listinfo/gnome-shell-list" />
<download-page rdf:resource="http://download.gnome.org/sources/gnome-shell/" />
<bug-database rdf:resource="http://bugzilla.gnome.org/browse.cgi?product=gnome-shell" />
<bug-database rdf:resource="https://bugzilla.gnome.org/browse.cgi?product=gnome-shell" />
<category rdf:resource="http://api.gnome.org/doap-extensions#desktop" />

View File

@ -1,124 +1,38 @@
NULL =
EXTRA_DIST = misc/config.js.in
CLEANFILES = misc/config.js
BUILT_SOURCES =
misc/config.js: misc/config.js.in Makefile
[ -d $(@D) ] || $(mkdir_p) $(@D) ; \
sed -e "s|[@]PACKAGE_NAME@|$(PACKAGE_NAME)|g" \
-e "s|[@]PACKAGE_VERSION@|$(PACKAGE_VERSION)|g" \
-e "s|[@]HAVE_BLUETOOTH@|$(HAVE_BLUETOOTH)|g" \
-e "s|[@]HAVE_NETWORKMANAGER@|$(HAVE_NETWORKMANAGER)|g" \
-e "s|[@]GETTEXT_PACKAGE@|$(GETTEXT_PACKAGE)|g" \
-e "s|[@]datadir@|$(datadir)|g" \
-e "s|[@]libexecdir@|$(libexecdir)|g" \
-e "s|[@]sysconfdir@|$(sysconfdir)|g" \
$< > $@
jsdir = $(pkgdatadir)/js
js_resource_files = $(shell $(GLIB_COMPILE_RESOURCES) --sourcedir=$(srcdir) --generate-dependencies $(srcdir)/js-resources.gresource.xml)
js-resources.h: js-resources.gresource.xml $(js_resource_files) misc/config.js
$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --sourcedir=$(builddir) --generate --c-name shell_js_resources $<
js-resources.c: js-resources.gresource.xml $(js_resource_files) misc/config.js
$(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=$(srcdir) --sourcedir=$(builddir) --generate --c-name shell_js_resources $<
nobase_dist_js_DATA = \
gdm/authPrompt.js \
gdm/batch.js \
gdm/fingerprint.js \
gdm/loginDialog.js \
gdm/realmd.js \
gdm/util.js \
extensionPrefs/main.js \
misc/config.js \
misc/extensionUtils.js \
misc/fileUtils.js \
misc/gnomeSession.js \
misc/hash.js \
misc/history.js \
misc/jsParse.js \
misc/loginManager.js \
misc/modemManager.js \
misc/objectManager.js \
misc/params.js \
misc/smartcardManager.js \
misc/util.js \
perf/core.js \
ui/altTab.js \
ui/animation.js \
ui/appDisplay.js \
ui/appFavorites.js \
ui/backgroundMenu.js \
ui/background.js \
ui/boxpointer.js \
ui/calendar.js \
ui/checkBox.js \
ui/ctrlAltTab.js \
ui/dash.js \
ui/dateMenu.js \
ui/dnd.js \
ui/endSessionDialog.js \
ui/extensionSystem.js \
ui/extensionDownloader.js \
ui/environment.js \
ui/focusCaretTracker.js\
ui/ibusCandidatePopup.js\
ui/grabHelper.js \
ui/iconGrid.js \
ui/keyboard.js \
ui/layout.js \
ui/lightbox.js \
ui/lookingGlass.js \
ui/magnifier.js \
ui/magnifierDBus.js \
ui/main.js \
ui/messageTray.js \
ui/modalDialog.js \
ui/separator.js \
ui/sessionMode.js \
ui/shellEntry.js \
ui/shellMountOperation.js \
ui/slider.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/remoteMenu.js \
ui/runDialog.js \
ui/screencast.js \
ui/screenshot.js \
ui/screenShield.js \
ui/scripting.js \
ui/search.js \
ui/searchDisplay.js \
ui/shellDBus.js \
ui/status/accessibility.js \
ui/status/brightness.js \
ui/status/keyboard.js \
ui/status/network.js \
ui/status/power.js \
ui/status/rfkill.js \
ui/status/volume.js \
ui/status/bluetooth.js \
ui/status/screencast.js \
ui/status/system.js \
ui/switcherPopup.js \
ui/tweener.js \
ui/unlockDialog.js \
ui/userWidget.js \
ui/viewSelector.js \
ui/wanda.js \
ui/windowAttentionHandler.js \
ui/windowManager.js \
ui/workspace.js \
ui/workspaceThumbnail.js \
ui/workspacesView.js \
ui/workspaceSwitcherPopup.js \
ui/xdndHandler.js \
ui/components/__init__.js \
ui/components/autorunManager.js \
ui/components/automountManager.js \
ui/components/networkAgent.js \
ui/components/polkitAgent.js \
ui/components/telepathyClient.js \
ui/components/keyring.js \
js_built_sources = js-resources.c js-resources.h
BUILT_SOURCES += $(js_built_sources)
all-local: $(js_built_sources)
js_resource_dist_files = $(filter-out misc/config.js, $(js_resource_files))
EXTRA_DIST = \
$(js_resource_dist_files) \
js-resources.gresource.xml \
misc/config.js.in \
$(NULL)
CLEANFILES = \
$(js_built_sources) \
$(NULL)

View File

@ -13,13 +13,20 @@ const _ = Gettext.gettext;
const Config = imports.misc.config;
const ExtensionUtils = imports.misc.extensionUtils;
const GnomeShellIface = <interface name="org.gnome.Shell.Extensions">
<signal name="ExtensionStatusChanged">
<arg type="s" name="uuid"/>
<arg type="i" name="state"/>
<arg type="s" name="error"/>
</signal>
</interface>;
const GnomeShellIface = '<node> \
<interface name="org.gnome.Shell.Extensions"> \
<signal name="ExtensionStatusChanged"> \
<arg type="s" name="uuid"/> \
<arg type="i" name="state"/> \
<arg type="s" name="error"/> \
</signal> \
</interface> \
</node>';
const customCss = '.prefs-button { \
padding: 8px; \
border-radius: 20px; \
}';
const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface);
@ -44,13 +51,9 @@ const Application = new Lang.Class({
this._extensionPrefsModules = {};
this._extensionIters = {};
this._startupUuid = null;
},
_buildModel: function() {
this._model = new Gtk.ListStore();
this._model.set_column_types([GObject.TYPE_STRING, GObject.TYPE_STRING]);
this._loaded = false;
this._skipMainWindow = false;
},
_extensionAvailable: function(uuid) {
@ -68,11 +71,6 @@ const Application = new Lang.Class({
return true;
},
_setExtensionInsensitive: function(layout, cell, model, iter, data) {
let uuid = model.get_value(iter, 0);
cell.set_sensitive(this._extensionAvailable(uuid));
},
_getExtensionPrefsModule: function(extension) {
let uuid = extension.metadata.uuid;
@ -102,21 +100,23 @@ const Application = new Lang.Class({
widget = this._buildErrorUI(extension, e);
}
// Destroy the current prefs widget, if it exists
if (this._extensionPrefsBin.get_child())
this._extensionPrefsBin.get_child().destroy();
let dialog = new Gtk.Dialog({ use_header_bar: true,
modal: true,
title: extension.metadata.name });
this._extensionPrefsBin.add(widget);
this._extensionSelector.set_active_iter(this._extensionIters[uuid]);
},
if (this._skipMainWindow) {
this.application.add_window(dialog);
if (this._window)
this._window.destroy();
this._window = dialog;
this._window.window_position = Gtk.WindowPosition.CENTER;
} else {
dialog.transient_for = this._window;
}
_extensionSelected: function() {
let [success, iter] = this._extensionSelector.get_active_iter();
if (!success)
return;
let uuid = this._model.get_value(iter, 0);
this._selectExtension(uuid);
dialog.set_default_size(600, 400);
dialog.get_content_area().add(widget);
dialog.show();
},
_buildErrorUI: function(extension, exc) {
@ -149,48 +149,26 @@ const Application = new Lang.Class({
_buildUI: function(app) {
this._window = new Gtk.ApplicationWindow({ application: app,
window_position: Gtk.WindowPosition.CENTER,
title: _("GNOME Shell Extension Preferences") });
window_position: Gtk.WindowPosition.CENTER });
this._window.set_size_request(600, 400);
this._window.set_size_request(800, 500);
let vbox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
this._window.add(vbox);
this._titlebar = new Gtk.HeaderBar({ show_close_button: true,
title: _("GNOME Shell Extensions") });
this._window.set_titlebar(this._titlebar);
let toolbar = new Gtk.Toolbar();
toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR);
vbox.add(toolbar);
let toolitem;
let scroll = new Gtk.ScrolledWindow({ hscrollbar_policy: Gtk.PolicyType.NEVER,
shadow_type: Gtk.ShadowType.IN,
halign: Gtk.Align.CENTER,
margin: 18 });
this._window.add(scroll);
let label = new Gtk.Label({ label: '<b>' + _("Extension") + '</b>',
use_markup: true });
toolitem = new Gtk.ToolItem({ child: label });
toolbar.add(toolitem);
this._extensionSelector = new Gtk.ListBox({ selection_mode: Gtk.SelectionMode.NONE });
this._extensionSelector.set_sort_func(Lang.bind(this, this._sortList));
this._extensionSelector.set_header_func(Lang.bind(this, this._updateHeader));
this._extensionSelector = new Gtk.ComboBox({ model: this._model,
margin_left: 8,
hexpand: true });
this._extensionSelector.get_style_context().add_class(Gtk.STYLE_CLASS_RAISED);
scroll.add(this._extensionSelector);
let renderer = new Gtk.CellRendererText();
this._extensionSelector.pack_start(renderer, true);
this._extensionSelector.add_attribute(renderer, 'text', 1);
this._extensionSelector.set_cell_data_func(renderer, Lang.bind(this, this._setExtensionInsensitive));
this._extensionSelector.connect('changed', Lang.bind(this, this._extensionSelected));
toolitem = new Gtk.ToolItem({ child: this._extensionSelector });
toolitem.set_expand(true);
toolbar.add(toolitem);
this._extensionPrefsBin = new Gtk.Frame();
vbox.add(this._extensionPrefsBin);
let label = new Gtk.Label({
label: _("Select an extension to configure using the combobox above."),
vexpand: true
});
this._extensionPrefsBin.add(label);
this._shellProxy = new GnomeShellProxy(Gio.DBus.session, 'org.gnome.Shell', '/org/gnome/Shell');
this._shellProxy.connectSignal('ExtensionStatusChanged', Lang.bind(this, function(proxy, senderName, [uuid, state, error]) {
@ -201,23 +179,61 @@ const Application = new Lang.Class({
this._window.show_all();
},
_addCustomStyle: function() {
let provider = new Gtk.CssProvider();
try {
provider.load_from_data(customCss, -1);
} catch(e) {
log('Failed to add application style');
return;
}
let screen = this._window.window.get_screen();
let priority = Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION;
Gtk.StyleContext.add_provider_for_screen(screen, provider, priority);
},
_sortList: function(row1, row2) {
let name1 = ExtensionUtils.extensions[row1.uuid].metadata.name;
let name2 = ExtensionUtils.extensions[row2.uuid].metadata.name;
return name1.localeCompare(name2);
},
_updateHeader: function(row, before) {
if (!before || row.get_header())
return;
let sep = new Gtk.Separator({ orientation: Gtk.Orientation.HORIZONTAL });
row.set_header(sep);
},
_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();
this._extensionsLoaded();
},
_extensionFound: function(signals, extension) {
let iter = this._model.append();
this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]);
this._extensionIters[extension.uuid] = iter;
_extensionFound: function(finder, extension) {
let row = new ExtensionRow(extension.uuid);
row.prefsButton.visible = this._extensionAvailable(row.uuid);
row.prefsButton.connect('clicked', Lang.bind(this,
function() {
this._selectExtension(row.uuid);
}));
row.show_all();
this._extensionSelector.add(row);
},
_extensionsLoaded: function() {
if (this._startupUuid && this._extensionAvailable(this._startupUuid))
this._selectExtension(this._startupUuid);
this._startupUuid = null;
this._skipMainWindow = false;
this._loaded = true;
},
_onActivate: function() {
@ -225,29 +241,125 @@ const Application = new Lang.Class({
},
_onStartup: function(app) {
this._buildModel();
this._buildUI(app);
this._addCustomStyle();
this._scanExtensions();
},
_onCommandLine: function(app, commandLine) {
app.activate();
let args = commandLine.get_arguments();
if (args.length) {
let uuid = args[0];
this._skipMainWindow = true;
// Strip off "extension:///" prefix which fakes a URI, if it exists
uuid = stripPrefix(uuid, "extension:///");
if (this._extensionAvailable(uuid))
this._selectExtension(uuid);
else
else if (!this._loaded)
this._startupUuid = uuid;
else
this._skipMainWindow = false;
}
return 0;
}
});
const ExtensionRow = new Lang.Class({
Name: 'ExtensionRow',
Extends: Gtk.ListBoxRow,
_init: function(uuid) {
this.parent();
this.uuid = uuid;
this._settings = new Gio.Settings({ schema: 'org.gnome.shell' });
this._settings.connect('changed::enabled-extensions', Lang.bind(this,
function() {
this._switch.state = this._isEnabled();
}));
this._buildUI();
},
_buildUI: function() {
let extension = ExtensionUtils.extensions[this.uuid];
let hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL,
hexpand: true, margin: 12, spacing: 6 });
this.add(hbox);
let vbox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL,
spacing: 6, hexpand: true });
hbox.add(vbox);
let name = GLib.markup_escape_text(extension.metadata.name, -1);
let label = new Gtk.Label({ label: '<b>' + name + '</b>',
use_markup: true,
halign: Gtk.Align.START });
vbox.add(label);
let desc = extension.metadata.description.split('\n')[0];
label = new Gtk.Label({ label: desc,
ellipsize: Pango.EllipsizeMode.END,
halign: Gtk.Align.START });
vbox.add(label);
let button = new Gtk.Button({ valign: Gtk.Align.CENTER,
no_show_all: true });
button.add(new Gtk.Image({ icon_name: 'emblem-system-symbolic',
icon_size: Gtk.IconSize.BUTTON,
visible: true }));
button.get_style_context().add_class('prefs-button');
hbox.add(button);
this.prefsButton = button;
this._switch = new Gtk.Switch({ valign: Gtk.Align.CENTER,
state: this._isEnabled() });
this._switch.connect('notify::active', Lang.bind(this,
function() {
if (this._switch.active)
this._enable();
else
this._disable();
}));
this._switch.connect('state-set', function() { return true; });
hbox.add(this._switch);
},
_isEnabled: function() {
let extensions = this._settings.get_strv('enabled-extensions');
return extensions.indexOf(this.uuid) != -1;
},
_enable: function() {
let extensions = this._settings.get_strv('enabled-extensions');
if (extensions.indexOf(this.uuid) != -1)
return;
extensions.push(this.uuid);
this._settings.set_strv('enabled-extensions', extensions);
},
_disable: function() {
let extensions = this._settings.get_strv('enabled-extensions');
let pos = extensions.indexOf(this.uuid);
if (pos == -1)
return;
do {
extensions.splice(pos, 1);
pos = extensions.indexOf(this.uuid);
} while (pos != -1);
this._settings.set_strv('enabled-extensions', extensions);
}
});
function initEnvironment() {
// Monkey-patch in a "global" object that fakes some Shell utilities
// that ExtensionUtils depends on.

View File

@ -59,6 +59,7 @@ const AuthPrompt = new Lang.Class({
this._userVerifier.connect('verification-complete', Lang.bind(this, this._onVerificationComplete));
this._userVerifier.connect('reset', Lang.bind(this, this._onReset));
this._userVerifier.connect('smartcard-status-changed', Lang.bind(this, this._onSmartcardStatusChanged));
this._userVerifier.connect('ovirt-user-authenticated', Lang.bind(this, this._onOVirtUserAuthenticated));
this.smartcardDetected = this._userVerifier.smartcardDetected;
this.connect('next', Lang.bind(this, function() {
@ -79,6 +80,7 @@ const AuthPrompt = new Lang.Class({
if (event.get_key_symbol() == Clutter.KEY_Escape) {
this.cancel();
}
return Clutter.EVENT_PROPAGATE;
}));
this._userWell = new St.Bin({ x_fill: true,
@ -92,7 +94,7 @@ const AuthPrompt = new Lang.Class({
this.actor.add(this._label,
{ expand: true,
x_fill: true,
x_fill: false,
y_fill: true,
x_align: St.Align.START });
this._entry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
@ -110,7 +112,7 @@ const AuthPrompt = new Lang.Class({
this._message = new St.Label({ opacity: 0,
styleClass: 'login-dialog-message' });
this._message.clutter_text.line_wrap = true;
this.actor.add(this._message, { x_fill: true, y_align: St.Align.START });
this.actor.add(this._message, { x_fill: false, x_align: St.Align.START, y_align: St.Align.START });
this._buttonBox = new St.BoxLayout({ style_class: 'login-dialog-button-box',
vertical: false });
@ -219,6 +221,11 @@ const AuthPrompt = new Lang.Class({
this.emit('prompted');
},
_onOVirtUserAuthenticated: function() {
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED)
this.reset();
},
_onSmartcardStatusChanged: function() {
this.smartcardDetected = this._userVerifier.smartcardDetected;
@ -257,10 +264,8 @@ const AuthPrompt = new Lang.Class({
},
_onReset: function() {
if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED) {
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
this.reset();
}
this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
this.reset();
},
addActorToDefaultButtonWell: function(actor) {
@ -444,10 +449,10 @@ const AuthPrompt = new Lang.Class({
// The user is constant at the unlock screen, so it will immediately
// respond to the request with the username
beginRequestType = BeginRequestType.PROVIDE_USERNAME;
} else if (this.smartcardDetected &&
} else if (this._userVerifier.serviceIsForeground(GdmUtil.OVIRT_SERVICE_NAME) ||
this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME)) {
// We don't need to know the username if the user preempted the login screen
// with a smartcard.
// with a smartcard or with preauthenticated oVirt credentials
beginRequestType = BeginRequestType.DONT_PROVIDE_USERNAME;
} else {
// In all other cases, we should get the username up front.

View File

@ -13,9 +13,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
const Lang = imports.lang;

View File

@ -5,11 +5,13 @@ const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const FprintManagerIface = <interface name='net.reactivated.Fprint.Manager'>
<method name='GetDefaultDevice'>
<arg type='o' direction='out' />
</method>
</interface>;
const FprintManagerIface = '<node> \
<interface name="net.reactivated.Fprint.Manager"> \
<method name="GetDefaultDevice"> \
<arg type="o" direction="out" /> \
</method> \
</interface> \
</node>';
const FprintManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(FprintManagerIface);

View File

@ -13,9 +13,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
const AccountsService = imports.gi.AccountsService;
@ -38,6 +36,7 @@ const BoxPointer = imports.ui.boxpointer;
const CtrlAltTab = imports.ui.ctrlAltTab;
const GdmUtil = imports.gdm.util;
const Layout = imports.ui.layout;
const LoginManager = imports.misc.loginManager;
const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const Realmd = imports.gdm.realmd;
@ -49,8 +48,6 @@ const _SCROLL_ANIMATION_TIME = 0.5;
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
const _LOGO_ICON_HEIGHT = 48;
let _loginDialog = null;
const UserListItem = new Lang.Class({
Name: 'UserListItem',
@ -59,7 +56,7 @@ const UserListItem = new Lang.Class({
this._userChangedId = this.user.connect('changed',
Lang.bind(this, this._onUserChanged));
let layout = new St.BoxLayout({ vertical: false });
let layout = new St.BoxLayout({ vertical: true });
this.actor = new St.Button({ style_class: 'login-dialog-user-list-item',
button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE,
can_focus: true,
@ -68,39 +65,18 @@ const UserListItem = new Lang.Class({
x_align: St.Align.START,
x_fill: true });
this._userAvatar = new UserWidget.Avatar(this.user,
{ styleClass: 'login-dialog-user-list-item-icon' });
layout.add(this._userAvatar.actor);
let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box',
vertical: true });
layout.add(textLayout, { expand: true });
this._nameLabel = new St.Label({ style_class: 'login-dialog-user-list-item-name' });
this.actor.label_actor = this._nameLabel;
textLayout.add(this._nameLabel,
{ y_fill: false,
y_align: St.Align.MIDDLE,
expand: true });
this._userWidget = new UserWidget.UserWidget(this.user);
layout.add(this._userWidget.actor);
this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator',
scale_x: 0 });
textLayout.add(this._timedLoginIndicator,
{ x_fill: true,
x_align: St.Align.MIDDLE,
y_fill: false,
y_align: St.Align.END });
layout.add(this._timedLoginIndicator);
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this._onUserChanged();
},
_onUserChanged: function() {
this._nameLabel.set_text(this.user.get_real_name());
this._userAvatar.update();
this._updateLoggedIn();
},
syncStyleClasses: function() {
this._updateLoggedIn();
},
@ -189,7 +165,6 @@ const UserList = new Lang.Class({
for (let userName in this._items) {
let item = this._items[userName];
item.actor.sync_hover();
item.syncStyleClasses();
}
},
@ -478,18 +453,6 @@ const LoginDialog = new Lang.Class({
this.actor.add_child(this._logoBin);
this._updateLogo();
if (!this._userManager.is_loaded)
this._userManagerLoadedId = this._userManager.connect('notify::is-loaded',
Lang.bind(this, function() {
if (this._userManager.is_loaded) {
this._loadUserList();
this._userManager.disconnect(this._userManagerLoadedId);
this._userManagerLoadedId = 0;
}
}));
else
this._loadUserList();
this._userList.connect('activate',
Lang.bind(this, function(userList, item) {
this._onUserListActivated(item);
@ -505,7 +468,33 @@ const LoginDialog = new Lang.Class({
this._sessionMenuButton.actor.show();
this._authPrompt.addActorToDefaultButtonWell(this._sessionMenuButton.actor);
},
this._disableUserList = undefined;
this._userListLoaded = false;
LoginManager.getLoginManager().getCurrentSessionProxy(Lang.bind(this, this._gotGreeterSessionProxy));
// If the user list is enabled, it should take key focus; make sure the
// screen shield is initialized first to prevent it from stealing the
// focus later
Main.layoutManager.connect('startup-complete',
Lang.bind(this, this._updateDisableUserList));
},
_ensureUserListLoaded: function() {
if (!this._userManager.is_loaded) {
this._userManagerLoadedId = this._userManager.connect('notify::is-loaded',
Lang.bind(this, function() {
if (this._userManager.is_loaded) {
this._loadUserList();
this._userManager.disconnect(this._userManagerLoadedId);
this._userManagerLoadedId = 0;
}
}));
} else {
let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, this._loadUserList));
GLib.Source.set_name_by_id(id, '[gnome-shell] _loadUserList');
}
},
_updateDisableUserList: function() {
let disableUserList = this._settings.get_boolean(GdmUtil.DISABLE_USER_LIST_KEY);
@ -548,9 +537,12 @@ const LoginDialog = new Lang.Class({
return;
this._logoBin.destroy_all_children();
if (this._logoFileUri)
if (this._logoFileUri) {
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
this._logoBin.add_child(this._textureCache.load_uri_async(this._logoFileUri,
-1, _LOGO_ICON_HEIGHT));
-1, _LOGO_ICON_HEIGHT,
scaleFactor));
}
},
_updateLogo: function() {
@ -619,7 +611,7 @@ const LoginDialog = new Lang.Class({
// Translators: this message is shown below the username entry field
// to clue the user in on how to login to the local network realm
this._authPrompt.setMessage(_("(e.g., user or %s)").format(hint), AuthPrompt.MessageType.HINT);
this._authPrompt.setMessage(_("(e.g., user or %s)").format(hint), GdmUtil.MessageType.HINT);
},
_askForUsernameAndBeginVerification: function() {
@ -649,6 +641,36 @@ const LoginDialog = new Lang.Class({
this._showPrompt();
},
_loginScreenSessionActivated: function() {
if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.VERIFICATION_SUCCEEDED)
return;
Tweener.addTween(this.actor,
{ opacity: 255,
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.actor.opacity;
}
},
onUpdateScope: this,
onComplete: function() {
this._authPrompt.reset();
},
onCompleteScope: this });
},
_gotGreeterSessionProxy: function(proxy) {
proxy.connect('g-properties-changed', Lang.bind(this, function() {
if (proxy.Active)
this._loginScreenSessionActivated();
}));
},
_startSession: function(serviceName) {
Tweener.addTween(this.actor,
{ opacity: 0,
@ -664,10 +686,11 @@ const LoginDialog = new Lang.Class({
},
onUpdateScope: this,
onComplete: function() {
Mainloop.idle_add(Lang.bind(this, function() {
let id = Mainloop.idle_add(Lang.bind(this, function() {
this._greeter.call_start_session_when_ready_sync(serviceName, true, null);
return false;
return GLib.SOURCE_REMOVE;
}));
GLib.Source.set_name_by_id(id, '[gnome-shell] this._greeter.call_start_session_when_ready_sync');
},
onCompleteScope: this });
},
@ -721,7 +744,9 @@ const LoginDialog = new Lang.Class({
function() {
this._timedLoginAnimationTime -= _TIMED_LOGIN_IDLE_THRESHOLD;
hold.release();
return GLib.SOURCE_REMOVE;
});
GLib.Source.set_name_by_id(this._timedLoginIdleTimeOutId, '[gnome-shell] this._timedLoginAnimationTime');
return hold;
},
@ -785,7 +810,7 @@ const LoginDialog = new Lang.Class({
global.stage.connect('captured-event',
Lang.bind(this, function(actor, event) {
if (this._timedLoginDelay == undefined)
return false;
return Clutter.EVENT_PROPAGATE;
if (event.type() == Clutter.EventType.KEY_PRESS ||
event.type() == Clutter.EventType.BUTTON_PRESS) {
@ -798,7 +823,7 @@ const LoginDialog = new Lang.Class({
this._resetTimedLogin();
}
return false;
return Clutter.EVENT_PROPAGATE;
}));
},
@ -824,6 +849,7 @@ const LoginDialog = new Lang.Class({
},
_showUserList: function() {
this._ensureUserListLoaded();
this._authPrompt.hide();
this._sessionMenuButton.close();
this._setUserListExpanded(true);
@ -867,14 +893,17 @@ const LoginDialog = new Lang.Class({
},
_loadUserList: function() {
if (this._userListLoaded)
return GLib.SOURCE_REMOVE;
this._userListLoaded = true;
let users = this._userManager.list_users();
for (let i = 0; i < users.length; i++) {
this._userList.addUser(users[i]);
}
this._updateDisableUserList();
this._userManager.connect('user-added',
Lang.bind(this, function(userManager, user) {
this._userList.addUser(user);
@ -884,6 +913,8 @@ const LoginDialog = new Lang.Class({
Lang.bind(this, function(userManager, user) {
this._userList.removeUser(user);
}));
return GLib.SOURCE_REMOVE;
},
open: function() {
@ -907,6 +938,10 @@ const LoginDialog = new Lang.Class({
Main.ctrlAltTabManager.removeGroup(this.dialogLayout);
},
cancel: function() {
this._authPrompt.cancel();
},
addCharacter: function(unichar) {
this._authPrompt.addCharacter(unichar);
},

64
js/gdm/oVirt.js Normal file
View File

@ -0,0 +1,64 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Signals = imports.signals;
const OVirtCredentialsIface = '<node> \
<interface name="org.ovirt.vdsm.Credentials"> \
<signal name="UserAuthenticated"> \
<arg type="s" name="token"/> \
</signal> \
</interface> \
</node>';
const OVirtCredentialsInfo = Gio.DBusInterfaceInfo.new_for_xml(OVirtCredentialsIface);
let _oVirtCredentialsManager = null;
function OVirtCredentials() {
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.system,
g_interface_name: OVirtCredentialsInfo.name,
g_interface_info: OVirtCredentialsInfo,
g_name: 'org.ovirt.vdsm.Credentials',
g_object_path: '/org/ovirt/vdsm/Credentials',
g_flags: (Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
self.init(null);
return self;
}
const OVirtCredentialsManager = new Lang.Class({
Name: 'OVirtCredentialsManager',
_init: function() {
this._token = null;
this._credentials = new OVirtCredentials();
this._credentials.connectSignal('UserAuthenticated',
Lang.bind(this, this._onUserAuthenticated));
},
_onUserAuthenticated: function(proxy, sender, [token]) {
this._token = token;
this.emit('user-authenticated', token);
},
hasToken: function() {
return this._token != null;
},
getToken: function() {
return this._token;
},
resetToken: function() {
this._token = null;
}
});
Signals.addSignalMethods(OVirtCredentialsManager.prototype);
function getOVirtCredentialsManager() {
if (!_oVirtCredentialsManager)
_oVirtCredentialsManager = new OVirtCredentialsManager();
return _oVirtCredentialsManager;
}

View File

@ -5,52 +5,58 @@ const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const ProviderIface = <interface name='org.freedesktop.realmd.Provider'>
<property name="Name" type="s" access="read"/>
<property name="Version" type="s" access="read"/>
<property name="Realms" type="ao" access="read"/>
<method name="Discover">
<arg name="string" type="s" direction="in"/>
<arg name="options" type="a{sv}" direction="in"/>
<arg name="relevance" type="i" direction="out"/>
<arg name="realm" type="ao" direction="out"/>
</method>
</interface>;
const ProviderIface = '<node> \
<interface name="org.freedesktop.realmd.Provider"> \
<property name="Name" type="s" access="read"/> \
<property name="Version" type="s" access="read"/> \
<property name="Realms" type="ao" access="read"/> \
<method name="Discover"> \
<arg name="string" type="s" direction="in"/> \
<arg name="options" type="a{sv}" direction="in"/> \
<arg name="relevance" type="i" direction="out"/> \
<arg name="realm" type="ao" direction="out"/> \
</method> \
</interface> \
</node>';
const Provider = Gio.DBusProxy.makeProxyWrapper(ProviderIface);
const ServiceIface = <interface name="org.freedesktop.realmd.Service">
<method name="Cancel">
<arg name="operation" type="s" direction="in"/>
</method>
<method name="Release" />
<method name="SetLocale">
<arg name="locale" type="s" direction="in"/>
</method>
<signal name="Diagnostics">
<arg name="data" type="s"/>
<arg name="operation" type="s"/>
</signal>
</interface>;
const ServiceIface = '<node> \
<interface name="org.freedesktop.realmd.Service"> \
<method name="Cancel"> \
<arg name="operation" type="s" direction="in"/> \
</method> \
<method name="Release" /> \
<method name="SetLocale"> \
<arg name="locale" type="s" direction="in"/> \
</method> \
<signal name="Diagnostics"> \
<arg name="data" type="s"/> \
<arg name="operation" type="s"/> \
</signal> \
</interface> \
</node>';
const Service = Gio.DBusProxy.makeProxyWrapper(ServiceIface);
const RealmIface = <interface name="org.freedesktop.realmd.Realm">
<property name="Name" type="s" access="read"/>
<property name="Configured" type="s" access="read"/>
<property name="Details" type="a(ss)" access="read"/>
<property name="LoginFormats" type="as" access="read"/>
<property name="LoginPolicy" type="s" access="read"/>
<property name="PermittedLogins" type="as" access="read"/>
<property name="SupportedInterfaces" type="as" access="read"/>
<method name="ChangeLoginPolicy">
<arg name="login_policy" type="s" direction="in"/>
<arg name="permitted_add" type="as" direction="in"/>
<arg name="permitted_remove" type="as" direction="in"/>
<arg name="options" type="a{sv}" direction="in"/>
</method>
<method name="Deconfigure">
<arg name="options" type="a{sv}" direction="in"/>
</method>
</interface>;
const RealmIface = '<node> \
<interface name="org.freedesktop.realmd.Realm"> \
<property name="Name" type="s" access="read"/> \
<property name="Configured" type="s" access="read"/> \
<property name="Details" type="a(ss)" access="read"/> \
<property name="LoginFormats" type="as" access="read"/> \
<property name="LoginPolicy" type="s" access="read"/> \
<property name="PermittedLogins" type="as" access="read"/> \
<property name="SupportedInterfaces" type="as" access="read"/> \
<method name="ChangeLoginPolicy"> \
<arg name="login_policy" type="s" direction="in"/> \
<arg name="permitted_add" type="as" direction="in"/> \
<arg name="permitted_remove" type="as" direction="in"/> \
<arg name="options" type="a{sv}" direction="in"/> \
</method> \
<method name="Deconfigure"> \
<arg name="options" type="a{sv}" direction="in"/> \
</method> \
</interface> \
</node>';
const Realm = Gio.DBusProxy.makeProxyWrapper(RealmIface);
const Manager = new Lang.Class({

View File

@ -10,6 +10,7 @@ const St = imports.gi.St;
const Batch = imports.gdm.batch;
const Fprint = imports.gdm.fingerprint;
const OVirt = imports.gdm.oVirt;
const Main = imports.ui.main;
const Params = imports.misc.params;
const ShellEntry = imports.ui.shellEntry;
@ -19,6 +20,7 @@ const Tweener = imports.ui.tweener;
const PASSWORD_SERVICE_NAME = 'gdm-password';
const FINGERPRINT_SERVICE_NAME = 'gdm-fingerprint';
const SMARTCARD_SERVICE_NAME = 'gdm-smartcard';
const OVIRT_SERVICE_NAME = 'gdm-ovirtcred';
const FADE_ANIMATION_TIME = 0.16;
const CLONE_FADE_ANIMATION_TIME = 0.25;
@ -151,6 +153,14 @@ const ShellUserVerifier = new Lang.Class({
this.reauthenticating = false;
this._failCounter = 0;
this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager();
if (this._oVirtCredentialsManager.hasToken())
this._oVirtUserAuthenticated(this._oVirtCredentialsManager.getToken());
this._oVirtCredentialsManager.connect('user-authenticated',
Lang.bind(this, this._oVirtUserAuthenticated));
},
begin: function(userName, hold) {
@ -240,7 +250,9 @@ const ShellUserVerifier = new Lang.Class({
Lang.bind(this, function() {
this._messageQueueTimeoutId = 0;
this._queueMessageTimeout();
return GLib.SOURCE_REMOVE;
}));
GLib.Source.set_name_by_id(this._messageQueueTimeoutId, '[gnome-shell] this._queueMessageTimeout');
},
_queueMessage: function(message, messageType) {
@ -277,12 +289,17 @@ const ShellUserVerifier = new Lang.Class({
}));
},
_oVirtUserAuthenticated: function(token) {
this._preemptingService = OVIRT_SERVICE_NAME;
this.emit('ovirt-user-authenticated');
},
_checkForSmartcard: function() {
let smartcardDetected;
if (!this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY))
smartcardDetected = false;
else if (this.reauthenticating)
else if (this._reauthOnly)
smartcardDetected = this._smartcardManager.hasInsertedLoginToken();
else
smartcardDetected = this._smartcardManager.hasInsertedTokens();
@ -455,6 +472,12 @@ const ShellUserVerifier = new Lang.Class({
if (!this.serviceIsForeground(serviceName))
return;
if (serviceName == OVIRT_SERVICE_NAME) {
// The only question asked by this service is "Token?"
this.answerQuery(serviceName, this._oVirtCredentialsManager.getToken());
return;
}
this.emit('ask-question', serviceName, secretQuestion, '\u25cf');
},
@ -515,6 +538,16 @@ const ShellUserVerifier = new Lang.Class({
},
_onConversationStopped: function(client, serviceName) {
// If the login failed with the preauthenticated oVirt credentials
// then discard the credentials and revert to default authentication
// mechanism.
if (this.serviceIsForeground(OVIRT_SERVICE_NAME)) {
this._oVirtCredentialsManager.resetToken();
this._preemptingService = null;
this._verificationFailed(false);
return;
}
// if the password service fails, then cancel everything.
// But if, e.g., fingerprint fails, still give
// password authentication a chance to succeed

View File

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/gnome/shell">
<file>gdm/authPrompt.js</file>
<file>gdm/batch.js</file>
<file>gdm/fingerprint.js</file>
<file>gdm/loginDialog.js</file>
<file>gdm/oVirt.js</file>
<file>gdm/realmd.js</file>
<file>gdm/util.js</file>
<file>extensionPrefs/main.js</file>
<file>misc/config.js</file>
<file>misc/extensionUtils.js</file>
<file>misc/fileUtils.js</file>
<file>misc/gnomeSession.js</file>
<file>misc/history.js</file>
<file>misc/jsParse.js</file>
<file>misc/loginManager.js</file>
<file>misc/modemManager.js</file>
<file>misc/objectManager.js</file>
<file>misc/params.js</file>
<file>misc/smartcardManager.js</file>
<file>misc/util.js</file>
<file>perf/core.js</file>
<file>ui/altTab.js</file>
<file>ui/animation.js</file>
<file>ui/appDisplay.js</file>
<file>ui/appFavorites.js</file>
<file>ui/backgroundMenu.js</file>
<file>ui/background.js</file>
<file>ui/boxpointer.js</file>
<file>ui/calendar.js</file>
<file>ui/checkBox.js</file>
<file>ui/ctrlAltTab.js</file>
<file>ui/dash.js</file>
<file>ui/dateMenu.js</file>
<file>ui/dnd.js</file>
<file>ui/endSessionDialog.js</file>
<file>ui/environment.js</file>
<file>ui/extensionDownloader.js</file>
<file>ui/extensionSystem.js</file>
<file>ui/focusCaretTracker.js</file>
<file>ui/grabHelper.js</file>
<file>ui/ibusCandidatePopup.js</file>
<file>ui/iconGrid.js</file>
<file>ui/keyboard.js</file>
<file>ui/layout.js</file>
<file>ui/lightbox.js</file>
<file>ui/lookingGlass.js</file>
<file>ui/magnifier.js</file>
<file>ui/magnifierDBus.js</file>
<file>ui/main.js</file>
<file>ui/messageTray.js</file>
<file>ui/modalDialog.js</file>
<file>ui/notificationDaemon.js</file>
<file>ui/osdWindow.js</file>
<file>ui/overview.js</file>
<file>ui/overviewControls.js</file>
<file>ui/panel.js</file>
<file>ui/panelMenu.js</file>
<file>ui/pointerWatcher.js</file>
<file>ui/popupMenu.js</file>
<file>ui/remoteMenu.js</file>
<file>ui/remoteSearch.js</file>
<file>ui/runDialog.js</file>
<file>ui/screenShield.js</file>
<file>ui/screencast.js</file>
<file>ui/screenshot.js</file>
<file>ui/scripting.js</file>
<file>ui/search.js</file>
<file>ui/separator.js</file>
<file>ui/sessionMode.js</file>
<file>ui/shellDBus.js</file>
<file>ui/shellEntry.js</file>
<file>ui/shellMountOperation.js</file>
<file>ui/slider.js</file>
<file>ui/switcherPopup.js</file>
<file>ui/tweener.js</file>
<file>ui/unlockDialog.js</file>
<file>ui/userWidget.js</file>
<file>ui/viewSelector.js</file>
<file>ui/windowAttentionHandler.js</file>
<file>ui/windowMenu.js</file>
<file>ui/windowManager.js</file>
<file>ui/workspace.js</file>
<file>ui/workspaceSwitcherPopup.js</file>
<file>ui/workspaceThumbnail.js</file>
<file>ui/workspacesView.js</file>
<file>ui/xdndHandler.js</file>
<file>ui/components/__init__.js</file>
<file>ui/components/autorunManager.js</file>
<file>ui/components/automountManager.js</file>
<file>ui/components/networkAgent.js</file>
<file>ui/components/polkitAgent.js</file>
<file>ui/components/telepathyClient.js</file>
<file>ui/components/keyring.js</file>
<file>ui/status/accessibility.js</file>
<file>ui/status/brightness.js</file>
<file>ui/status/location.js</file>
<file>ui/status/keyboard.js</file>
<file>ui/status/network.js</file>
<file>ui/status/power.js</file>
<file>ui/status/rfkill.js</file>
<file>ui/status/volume.js</file>
<file>ui/status/bluetooth.js</file>
<file>ui/status/screencast.js</file>
<file>ui/status/system.js</file>
</gresource>
</gresources>

View File

@ -6,6 +6,8 @@ const PACKAGE_NAME = '@PACKAGE_NAME@';
const PACKAGE_VERSION = '@PACKAGE_VERSION@';
/* 1 if gnome-bluetooth is available, 0 otherwise */
const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;
/* 1 if networkmanager is available, 0 otherwise */
const HAVE_NETWORKMANAGER = @HAVE_NETWORKMANAGER@;
/* gettext package */
const GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@';
/* locale dir */

View File

@ -174,17 +174,9 @@ const ExtensionFinder = new Lang.Class({
this.emit('extension-found', extension);
},
_extensionsLoaded: function() {
this.emit('extensions-loaded');
},
scanExtensions: function() {
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 });
FileUtils.collectFromDatadirs('extensions', true, Lang.bind(this, this._loadExtension, perUserDir));
}
});
Signals.addSignalMethods(ExtensionFinder.prototype);

View File

@ -5,80 +5,27 @@ const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Params = imports.misc.params;
function listDirAsync(file, callback) {
let allFiles = [];
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);
function onNextFileComplete(obj, res) {
let files = obj.next_files_finish(res);
if (files.length) {
allFiles = allFiles.concat(files);
enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, onNextFileComplete);
} else {
enumerator.close(null);
callback(allFiles);
}
}
enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, onNextFileComplete);
});
}
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;
}
function collectFromDatadirs(subdir, includeUserDir, processFile) {
let dataDirs = GLib.get_system_data_dirs();
if (params.includeUserDir)
if (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);
let fileEnum;
try {
fileEnum = dir.enumerate_children('standard::name,standard::type',
Gio.FileQueryInfoFlags.NONE, null);
} catch (e) {
fileEnum = null;
}
if (fileEnum != null) {
let info;
while ((info = fileEnum.next_file(null)))
processFile(fileEnum.get_child(info), info);
}
}
}

View File

@ -4,15 +4,17 @@ const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Signals = imports.signals;
const PresenceIface = <interface name="org.gnome.SessionManager.Presence">
<method name="SetStatus">
<arg type="u" direction="in"/>
</method>
<property name="status" type="u" access="readwrite"/>
<signal name="StatusChanged">
<arg type="u" direction="out"/>
</signal>
</interface>;
const PresenceIface = '<node> \
<interface name="org.gnome.SessionManager.Presence"> \
<method name="SetStatus"> \
<arg type="u" direction="in"/> \
</method> \
<property name="status" type="u" access="readwrite"/> \
<signal name="StatusChanged"> \
<arg type="u" direction="out"/> \
</signal> \
</interface> \
</node>';
const PresenceStatus = {
AVAILABLE: 0,
@ -30,14 +32,16 @@ function Presence(initCallback, cancellable) {
// Note inhibitors are immutable objects, so they don't
// change at runtime (changes always come in the form
// of new inhibitors)
const InhibitorIface = <interface name="org.gnome.SessionManager.Inhibitor">
<method name="GetAppId">
<arg type="s" direction="out" />
</method>
<method name="GetReason">
<arg type="s" direction="out" />
</method>
</interface>;
const InhibitorIface = '<node> \
<interface name="org.gnome.SessionManager.Inhibitor"> \
<method name="GetAppId"> \
<arg type="s" direction="out" /> \
</method> \
<method name="GetReason"> \
<arg type="s" direction="out" /> \
</method> \
</interface> \
</node>';
var InhibitorProxy = Gio.DBusProxy.makeProxyWrapper(InhibitorIface);
function Inhibitor(objectPath, initCallback, cancellable) {
@ -45,27 +49,29 @@ function Inhibitor(objectPath, initCallback, cancellable) {
}
// Not the full interface, only the methods we use
const SessionManagerIface = <interface name="org.gnome.SessionManager">
<method name="Logout">
<arg type="u" direction="in" />
</method>
<method name="Shutdown" />
<method name="Reboot" />
<method name="CanShutdown">
<arg type="b" direction="out" />
</method>
<method name="IsInhibited">
<arg type="u" direction="in" />
<arg type="b" direction="out" />
</method>
<property name="SessionIsActive" type="b" access="read"/>
<signal name="InhibitorAdded">
<arg type="o" direction="out"/>
</signal>
<signal name="InhibitorRemoved">
<arg type="o" direction="out"/>
</signal>
</interface>;
const SessionManagerIface = '<node> \
<interface name="org.gnome.SessionManager"> \
<method name="Logout"> \
<arg type="u" direction="in" /> \
</method> \
<method name="Shutdown" /> \
<method name="Reboot" /> \
<method name="CanShutdown"> \
<arg type="b" direction="out" /> \
</method> \
<method name="IsInhibited"> \
<arg type="u" direction="in" /> \
<arg type="b" direction="out" /> \
</method> \
<property name="SessionIsActive" type="b" access="read"/> \
<signal name="InhibitorAdded"> \
<arg type="o" direction="out"/> \
</signal> \
<signal name="InhibitorRemoved"> \
<arg type="o" direction="out"/> \
</signal> \
</interface> \
</node>';
var SessionManagerProxy = Gio.DBusProxy.makeProxyWrapper(SessionManagerIface);
function SessionManager(initCallback, cancellable) {

View File

@ -1,144 +0,0 @@
// -*- 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 = { };
this._size = 0;
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 };
this._size++;
}
},
delete: function(key) {
let hash = this._hashKey(key);
let node = this._pool[hash];
if (node && _sameValue(node.key, key)) {
delete this._pool[hash];
this._size--;
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 this._size;
},
});

View File

@ -89,7 +89,7 @@ const HistoryManager = new Lang.Class({
} else if (symbol == Clutter.KEY_Down) {
return this._setNextItem(entry.get_text());
}
return false;
return Clutter.EVENT_PROPAGATE;
},
_indexChanged: function() {

View File

@ -7,62 +7,45 @@ const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const SystemdLoginManagerIface = <interface name='org.freedesktop.login1.Manager'>
<method name='Suspend'>
<arg type='b' direction='in'/>
</method>
<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='GetSession'>
<arg type='s' direction='in'/>
<arg type='o' 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 SystemdLoginManagerIface = '<node> \
<interface name="org.freedesktop.login1.Manager"> \
<method name="Suspend"> \
<arg type="b" direction="in"/> \
</method> \
<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="GetSession"> \
<arg type="s" direction="in"/> \
<arg type="o" 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> \
</node>';
const SystemdLoginSessionIface = <interface name='org.freedesktop.login1.Session'>
<signal name='Lock' />
<signal name='Unlock' />
</interface>;
const SystemdLoginSessionIface = '<node> \
<interface name="org.freedesktop.login1.Session"> \
<signal name="Lock" /> \
<signal name="Unlock" /> \
<property name="Active" type="b" access="read" /> \
</interface> \
</node>';
const SystemdLoginManager = Gio.DBusProxy.makeProxyWrapper(SystemdLoginManagerIface);
const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIface);
const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manager'>
<method name='CanRestart'>
<arg type='b' direction='out'/>
</method>
<method name='CanStop'>
<arg type='b' direction='out'/>
</method>
<method name='Restart' />
<method name='Stop' />
<method name='GetCurrentSession'>
<arg type='o' direction='out' />
</method>
</interface>;
const ConsoleKitSessionIface = <interface name='org.freedesktop.ConsoleKit.Session'>
<signal name='Lock' />
<signal name='Unlock' />
</interface>;
const ConsoleKitSession = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface);
const ConsoleKitManager = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);
function haveSystemd() {
return GLib.access("/run/systemd/seats", 0) >= 0;
}
@ -92,7 +75,7 @@ function canLock() {
-1, null);
let version = result.deep_unpack()[0].deep_unpack();
return versionCompare('3.5.91', version);
return haveSystemd() && versionCompare('3.5.91', version);
} catch(e) {
return false;
}
@ -110,7 +93,7 @@ function getLoginManager() {
if (haveSystemd())
_loginManager = new LoginManagerSystemd();
else
_loginManager = new LoginManagerConsoleKit();
_loginManager = new LoginManagerDummy();
}
return _loginManager;
@ -127,9 +110,6 @@ const LoginManagerSystemd = new Lang.Class({
Lang.bind(this, this._prepareForSleep));
},
// Having this function is a bit of a hack since the Systemd and ConsoleKit
// session objects have different interfaces - but in both cases there are
// Lock/Unlock signals, and that's all we count upon at the moment.
getCurrentSessionProxy: function(callback) {
if (this._currentSession) {
callback (this._currentSession);
@ -182,7 +162,7 @@ const LoginManagerSystemd = new Lang.Class({
let fd = -1;
try {
let [outVariant, fdList] = proxy.call_with_unix_fd_list_finish(result);
fd = fdList.steal_fds(outVariant.deep_unpack())[0];
fd = fdList.steal_fds()[0];
callback(new Gio.UnixInputStream({ fd: fd }));
} catch(e) {
logError(e, "Error getting systemd inhibitor");
@ -197,35 +177,13 @@ const LoginManagerSystemd = new Lang.Class({
});
Signals.addSignalMethods(LoginManagerSystemd.prototype);
const LoginManagerConsoleKit = new Lang.Class({
Name: 'LoginManagerConsoleKit',
const LoginManagerDummy = new Lang.Class({
Name: 'LoginManagerDummy',
_init: function() {
this._proxy = new ConsoleKitManager(Gio.DBus.system,
'org.freedesktop.ConsoleKit',
'/org/freedesktop/ConsoleKit/Manager');
},
// Having this function is a bit of a hack since the Systemd and ConsoleKit
// session objects have different interfaces - but in both cases there are
// Lock/Unlock signals, and that's all we count upon at the moment.
getCurrentSessionProxy: function(callback) {
if (this._currentSession) {
callback (this._currentSession);
return;
}
this._proxy.GetCurrentSessionRemote(Lang.bind(this,
function(result, error) {
if (error) {
logError(error, 'Could not get a proxy for the current session');
} else {
this._currentSession = new ConsoleKitSession(Gio.DBus.system,
'org.freedesktop.ConsoleKit',
result[0]);
callback(this._currentSession);
}
}));
// we could return a DummySession object that fakes whatever callers
// expect (at the time of writing: connect() and connectSignal()
// methods), but just never calling the callback should be safer
},
canSuspend: function(asyncCallback) {
@ -245,4 +203,4 @@ const LoginManagerConsoleKit = new Lang.Class({
callback(null);
}
});
Signals.addSignalMethods(LoginManagerConsoleKit.prototype);
Signals.addSignalMethods(LoginManagerDummy.prototype);

View File

@ -92,37 +92,41 @@ function _findProviderForSid(sid) {
// The following are not the complete interfaces, just the methods we need
// (or may need in the future)
const ModemGsmNetworkInterface = <interface name="org.freedesktop.ModemManager.Modem.Gsm.Network">
<method name="GetRegistrationInfo">
<arg type="(uss)" direction="out" />
</method>
<method name="GetSignalQuality">
<arg type="u" direction="out" />
</method>
<property name="AccessTechnology" type="u" access="read" />
<signal name="SignalQuality">
<arg type="u" direction="out" />
</signal>
<signal name="RegistrationInfo">
<arg type="u" direction="out" />
<arg type="s" direction="out" />
<arg type="s" direction="out" />
</signal>
</interface>;
const ModemGsmNetworkInterface = '<node> \
<interface name="org.freedesktop.ModemManager.Modem.Gsm.Network"> \
<method name="GetRegistrationInfo"> \
<arg type="(uss)" direction="out" /> \
</method> \
<method name="GetSignalQuality"> \
<arg type="u" direction="out" /> \
</method> \
<property name="AccessTechnology" type="u" access="read" /> \
<signal name="SignalQuality"> \
<arg type="u" direction="out" /> \
</signal> \
<signal name="RegistrationInfo"> \
<arg type="u" direction="out" /> \
<arg type="s" direction="out" /> \
<arg type="s" direction="out" /> \
</signal> \
</interface> \
</node>';
const ModemGsmNetworkProxy = Gio.DBusProxy.makeProxyWrapper(ModemGsmNetworkInterface);
const ModemCdmaInterface = <interface name="org.freedesktop.ModemManager.Modem.Cdma">
<method name="GetSignalQuality">
<arg type="u" direction="out" />
</method>
<method name="GetServingSystem">
<arg type="(usu)" direction="out" />
</method>
<signal name="SignalQuality">
<arg type="u" direction="out" />
</signal>
</interface>;
const ModemCdmaInterface = '<node> \
<interface name="org.freedesktop.ModemManager.Modem.Cdma"> \
<method name="GetSignalQuality"> \
<arg type="u" direction="out" /> \
</method> \
<method name="GetServingSystem"> \
<arg type="(usu)" direction="out" /> \
</method> \
<signal name="SignalQuality"> \
<arg type="u" direction="out" /> \
</signal> \
</interface> \
</node>';
const ModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(ModemCdmaInterface);
@ -218,20 +222,26 @@ 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 BroadbandModemInterface = '<node> \
<interface name="org.freedesktop.ModemManager1.Modem"> \
<property name="SignalQuality" type="(ub)" access="read" /> \
</interface> \
</node>';
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 BroadbandModem3gppInterface = '<node> \
<interface name="org.freedesktop.ModemManager1.Modem.Modem3gpp"> \
<property name="OperatorCode" type="s" access="read" /> \
<property name="OperatorName" type="s" access="read" /> \
</interface> \
</node>';
const BroadbandModem3gppProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModem3gppInterface);
const BroadbandModemCdmaInterface = <interface name="org.freedesktop.ModemManager1.Modem.ModemCdma">
<property name="Sid" type="u" access="read" />
</interface>;
const BroadbandModemCdmaInterface = '<node> \
<interface name="org.freedesktop.ModemManager1.Modem.ModemCdma"> \
<property name="Sid" type="u" access="read" /> \
</interface> \
</node>';
const BroadbandModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(BroadbandModemCdmaInterface);
const BroadbandModem = new Lang.Class({

View File

@ -8,19 +8,21 @@ const Signals = imports.signals;
// Specified in the D-Bus specification here:
// http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager
const ObjectManagerIface = <interface name="org.freedesktop.DBus.ObjectManager">
<method name="GetManagedObjects">
<arg name="objects" type="a{oa{sa{sv}}}" direction="out"/>
</method>
<signal name="InterfacesAdded">
<arg name="objectPath" type="o"/>
<arg name="interfaces" type="a{sa{sv}}" />
</signal>
<signal name="InterfacesRemoved">
<arg name="objectPath" type="o"/>
<arg name="interfaces" type="as" />
</signal>
</interface>;
const ObjectManagerIface = '<node> \
<interface name="org.freedesktop.DBus.ObjectManager"> \
<method name="GetManagedObjects"> \
<arg name="objects" type="a{oa{sa{sv}}}" direction="out"/> \
</method> \
<signal name="InterfacesAdded"> \
<arg name="objectPath" type="o"/> \
<arg name="interfaces" type="a{sa{sv}}" /> \
</signal> \
<signal name="InterfacesRemoved"> \
<arg name="objectPath" type="o"/> \
<arg name="interfaces" type="as" /> \
</signal> \
</interface> \
</node>';
const ObjectManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ObjectManagerIface);

View File

@ -7,12 +7,14 @@ const Signals = imports.signals;
const ObjectManager = imports.misc.objectManager;
const SmartcardTokenIface = <interface name="org.gnome.SettingsDaemon.Smartcard.Token">
<property name="Name" type="s" access="read"/>
<property name="Driver" type="o" access="read"/>
<property name="IsInserted" type="b" access="read"/>
<property name="UsedToLogin" type="b" access="read"/>
</interface>;
const SmartcardTokenIface = '<node> \
<interface name="org.gnome.SettingsDaemon.Smartcard.Token"> \
<property name="Name" type="s" access="read"/> \
<property name="Driver" type="o" access="read"/> \
<property name="IsInserted" type="b" access="read"/> \
<property name="UsedToLogin" type="b" access="read"/> \
</interface> \
</node>';
let _smartcardManager = null;

View File

@ -1,6 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const St = imports.gi.St;
@ -79,6 +80,22 @@ function spawnCommandLine(command_line) {
}
}
// spawnApp:
// @argv: an argv array
//
// Runs @argv as if it was an application, handling startup notification
function spawnApp(argv) {
try {
let app = Gio.AppInfo.create_from_commandline(argv.join(' '), null,
Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION);
let context = global.create_app_launch_context(0, -1);
app.launch([], context);
} catch(err) {
_handleSpawnError(argv[0], err);
}
}
// trySpawn:
// @argv: an argv array
//
@ -112,7 +129,7 @@ function trySpawn(argv)
// Dummy child watch; we don't want to double-fork internally
// because then we lose the parent-child relationship, which
// can break polkit. See https://bugzilla.redhat.com//show_bug.cgi?id=819275
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function () {}, null);
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function () {});
}
// trySpawnCommandLine:
@ -136,7 +153,7 @@ function trySpawnCommandLine(command_line) {
}
function _handleSpawnError(command, err) {
let title = _("Execution of '%s' failed:").format(command);
let title = _("Execution of %s failed:").format(command);
Main.notifyError(title, err.message);
}

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 Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@ -23,7 +24,7 @@ const WINDOW_PREVIEW_SIZE = 128;
const APP_ICON_SIZE = 96;
const APP_ICON_SIZE_SMALL = 48;
const iconSizes = [96, 64, 48, 32, 22];
const baseIconSizes = [96, 64, 48, 32, 22];
const AppIconMode = {
THUMBNAIL_ONLY: 1,
@ -106,6 +107,8 @@ const AppSwitcherPopup = new Lang.Class({
this._switcherList = new AppSwitcher(apps, this);
this._items = this._switcherList.icons;
if (this._items.length == 0)
return false;
return true;
},
@ -164,6 +167,8 @@ const AppSwitcherPopup = new Lang.Class({
this._select(this._selectedIndex, this._nextWindow());
else if (keysym == Clutter.Up)
this._select(this._selectedIndex, null, true);
else
return Clutter.EVENT_PROPAGATE;
} else {
if (keysym == Clutter.Left)
this._select(this._previous());
@ -171,7 +176,11 @@ const AppSwitcherPopup = new Lang.Class({
this._select(this._next());
else if (keysym == Clutter.Down)
this._select(this._selectedIndex, 0);
else
return Clutter.EVENT_PROPAGATE;
}
return Clutter.EVENT_STOP;
},
_scrollHandler: function(direction) {
@ -302,6 +311,7 @@ const AppSwitcherPopup = new Lang.Class({
this._thumbnailTimeoutId = Mainloop.timeout_add (
THUMBNAIL_POPUP_TIME,
Lang.bind(this, this._timeoutPopupThumbnails));
GLib.Source.set_name_by_id(this._thumbnailTimeoutId, '[gnome-shell] this._timeoutPopupThumbnails');
}
},
@ -310,7 +320,7 @@ const AppSwitcherPopup = new Lang.Class({
this._createThumbnails();
this._thumbnailTimeoutId = 0;
this._thumbnailsFocused = false;
return false;
return GLib.SOURCE_REMOVE;
},
_destroyThumbnails : function() {
@ -362,7 +372,7 @@ const WindowSwitcherPopup = new Lang.Class({
_getWindowList: function() {
let workspace = this._settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace() : null;
return global.display.get_tab_list(Meta.TabList.NORMAL, global.screen, workspace);
return global.display.get_tab_list(Meta.TabList.NORMAL, workspace);
},
_createSwitcher: function() {
@ -375,6 +385,9 @@ const WindowSwitcherPopup = new Lang.Class({
this._switcherList = new WindowList(windows, mode);
this._items = this._switcherList.icons;
if (this._items.length == 0)
return false;
return true;
},
@ -397,7 +410,11 @@ const WindowSwitcherPopup = new Lang.Class({
this._select(this._previous());
else if (keysym == Clutter.Right)
this._select(this._next());
else
return Clutter.EVENT_PROPAGATE;
}
return Clutter.EVENT_STOP;
},
_finish: function() {
@ -424,7 +441,6 @@ const AppIcon = new Lang.Class({
set_size: function(size) {
this.icon = this.app.create_icon_texture(size);
this._iconBin.set_size(size, size);
this._iconBin.child = this.icon;
}
});
@ -443,8 +459,7 @@ const AppSwitcher = new Lang.Class({
let settings = new Gio.Settings({ schema: 'org.gnome.shell.app-switcher' });
let workspace = settings.get_boolean('current-workspace-only') ? global.screen.get_active_workspace()
: null;
let allWindows = global.display.get_tab_list(Meta.TabList.NORMAL,
global.screen, workspace);
let allWindows = global.display.get_tab_list(Meta.TabList.NORMAL, workspace);
// Construct the AppIcons, add to the popup
for (let i = 0; i < apps.length; i++) {
@ -454,9 +469,10 @@ const AppSwitcher = new Lang.Class({
appIcon.cachedWindows = allWindows.filter(function(w) {
return windowTracker.get_window_app (w) == appIcon.app;
});
if (workspace == null || appIcon.cachedWindows.length > 0) {
if (appIcon.cachedWindows.length > 0)
this._addIcon(appIcon);
}
else if (workspace == null)
throw new Error('%s appears to be running, but doesn\'t have any windows'.format(appIcon.app.get_name()));
}
this._curApp = -1;
@ -472,12 +488,13 @@ const AppSwitcher = new Lang.Class({
Mainloop.source_remove(this._mouseTimeOutId);
},
_getPreferredHeight: function (actor, forWidth, alloc) {
_setIconSize: function() {
let j = 0;
while(this._items.length > 1 && this._items[j].style_class != 'item-box') {
j++;
}
let themeNode = this._items[j].get_theme_node();
let iconPadding = themeNode.get_horizontal_padding();
let iconBorder = themeNode.get_border_width(St.Side.LEFT) + themeNode.get_border_width(St.Side.RIGHT);
let [iconMinHeight, iconNaturalHeight] = this.icons[j].label.get_preferred_height(-1);
@ -488,19 +505,22 @@ const AppSwitcher = new Lang.Class({
let primary = Main.layoutManager.primaryMonitor;
let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding();
let availWidth = primary.width - parentPadding - this.actor.get_theme_node().get_horizontal_padding();
let height = 0;
for(let i = 0; i < iconSizes.length; i++) {
this._iconSize = iconSizes[i];
height = iconSizes[i] + iconSpacing;
let w = height * this._items.length + totalSpacing;
if (w <= availWidth)
break;
}
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
let iconSizes = baseIconSizes.map(function(s) {
return s * scaleFactor;
});
if (this._items.length == 1) {
this._iconSize = iconSizes[0];
height = iconSizes[0] + iconSpacing;
this._iconSize = baseIconSizes[0];
} else {
for(let i = 0; i < baseIconSizes.length; i++) {
this._iconSize = baseIconSizes[i];
let height = iconSizes[i] + iconSpacing;
let w = height * this._items.length + totalSpacing;
if (w <= availWidth)
break;
}
}
for(let i = 0; i < this.icons.length; i++) {
@ -508,9 +528,11 @@ const AppSwitcher = new Lang.Class({
break;
this.icons[i].set_size(this._iconSize);
}
},
alloc.min_size = height;
alloc.natural_size = height;
_getPreferredHeight: function (actor, forWidth, alloc) {
this._setIconSize();
this.parent(actor, forWidth, alloc);
},
_allocate: function (actor, box, flags) {
@ -542,8 +564,9 @@ const AppSwitcher = new Lang.Class({
Lang.bind(this, function () {
this._enterItem(index);
this._mouseTimeOutId = 0;
return false;
return GLib.SOURCE_REMOVE;
}));
GLib.Source.set_name_by_id(this._mouseTimeOutId, '[gnome-shell] this._enterItem');
} else
this._itemEntered(index);
},
@ -643,17 +666,19 @@ const ThumbnailList = new Lang.Class({
totalPadding += this.actor.get_theme_node().get_horizontal_padding() + this.actor.get_theme_node().get_vertical_padding();
let [labelMinHeight, labelNaturalHeight] = this._labels[0].get_preferred_height(-1);
let spacing = this._items[0].child.get_theme_node().get_length('spacing');
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
let thumbnailSize = THUMBNAIL_DEFAULT_SIZE * scaleFactor;
availHeight = Math.min(availHeight - labelNaturalHeight - totalPadding - spacing, THUMBNAIL_DEFAULT_SIZE);
availHeight = Math.min(availHeight - labelNaturalHeight - totalPadding - spacing, thumbnailSize);
let binHeight = availHeight + this._items[0].get_theme_node().get_vertical_padding() + this.actor.get_theme_node().get_vertical_padding() - spacing;
binHeight = Math.min(THUMBNAIL_DEFAULT_SIZE, binHeight);
binHeight = Math.min(thumbnailSize, binHeight);
for (let i = 0; i < this._thumbnailBins.length; i++) {
let mutterWindow = this._windows[i].get_compositor_private();
if (!mutterWindow)
continue;
let clone = _createWindowClone(mutterWindow, THUMBNAIL_DEFAULT_SIZE);
let clone = _createWindowClone(mutterWindow, thumbnailSize);
this._thumbnailBins[i].set_height(binHeight);
this._thumbnailBins[i].add_actor(clone);
this._clones.push(clone);

View File

@ -1,5 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const St = imports.gi.St;
@ -20,7 +21,9 @@ const Animation = new Lang.Class({
this._isPlaying = false;
this._timeoutId = 0;
this._frame = 0;
this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height,
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height, scaleFactor,
Lang.bind(this, this._animationsLoaded));
this.actor.set_child(this._animations);
},
@ -31,6 +34,7 @@ const Animation = new Lang.Class({
this._showFrame(0);
this._timeoutId = Mainloop.timeout_add(this._speed, Lang.bind(this, this._update));
GLib.Source.set_name_by_id(this._timeoutId, '[gnome-shell] this._update');
}
this._isPlaying = true;
@ -59,7 +63,7 @@ const Animation = new Lang.Class({
_update: function() {
this._showFrame(this._frame + 1);
return true;
return GLib.SOURCE_CONTINUE;
},
_animationsLoaded: function() {

View File

@ -5,7 +5,6 @@ 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;
const Lang = imports.lang;
const Signals = imports.signals;
@ -42,29 +41,47 @@ const MIN_FREQUENT_APPS_COUNT = 3;
const INDICATORS_BASE_TIME = 0.25;
const INDICATORS_ANIMATION_DELAY = 0.125;
const INDICATORS_ANIMATION_MAX_TIME = 0.75;
// Fraction of page height the finger or mouse must reach
// to change page
const PAGE_SWITCH_TRESHOLD = 0.2;
const PAGE_SWITCH_TIME = 0.3;
// 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 VIEWS_SWITCH_TIME = 0.4;
const VIEWS_SWITCH_ANIMATION_DELAY = 0.1;
function _getCategories(info) {
let categoriesStr = info.get_categories();
if (!categoriesStr)
return [];
return categoriesStr.split(';');
}
function _listsIntersect(a, b) {
for (let itemA of a)
if (b.indexOf(itemA) >= 0)
return true;
return false;
}
function _getFolderName(folder) {
let name = folder.get_string('name');
if (folder.get_boolean('translate')) {
let keyfile = new GLib.KeyFile();
let path = 'desktop-directories/' + name;
try {
keyfile.load_from_data_dirs(path, GLib.KeyFileFlags.NONE);
name = keyfile.get_locale_string('Desktop Entry', 'Name', null);
} catch(e) {
return name;
}
}
};
return name;
}
function clamp(value, min, max) {
return Math.max(min, Math.min(max, value));
}
const BaseAppView = new Lang.Class({
Name: 'BaseAppView',
@ -84,6 +101,9 @@ const BaseAppView = new Lang.Class({
else
this._grid = new IconGrid.IconGrid(gridParams);
this._grid.connect('key-focus-in', Lang.bind(this, function(grid, actor) {
this._keyFocusIn(actor);
}));
// Standard hack for ClutterBinLayout
this._grid.actor.x_expand = true;
@ -91,46 +111,43 @@ const BaseAppView = new Lang.Class({
this._allItems = [];
},
_keyFocusIn: function(actor) {
// Nothing by default
},
removeAll: function() {
this._grid.removeAll();
this._grid.destroyAll();
this._items = {};
this._allItems = [];
},
_getItemId: function(item) {
throw new Error('Not implemented');
_redisplay: function() {
this.removeAll();
this._loadApps();
},
_createItemIcon: function(item) {
throw new Error('Not implemented');
getAllItems: function() {
return this._allItems;
},
addItem: function(icon) {
let id = icon.id;
if (this._items[id] !== undefined)
return;
this._allItems.push(icon);
this._items[id] = icon;
},
_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;
return a.name.localeCompare(b.name);
},
loadGrid: function() {
this._allItems.sort(Lang.bind(this, 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]);
}
this._allItems.sort(this._compareItems);
this._allItems.forEach(Lang.bind(this, function(item) {
this._grid.addItem(item);
}));
this.emit('view-loaded');
},
@ -243,7 +260,8 @@ const PageIndicators = new Lang.Class({
Tweener.addTween(children[i],
{ translation_x: 0,
time: INDICATORS_BASE_TIME + delay * i,
transition: 'easeInOutQuad'
transition: 'easeInOutQuad',
delay: VIEWS_SWITCH_ANIMATION_DELAY
});
}
}
@ -281,7 +299,7 @@ const AllView = new Lang.Class({
this._pageIndicators.actor.connect('scroll-event', Lang.bind(this, this._onScroll));
this.actor.add_actor(this._pageIndicators.actor);
this._folderIcons = [];
this.folderIcons = [];
this._stack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
let box = new St.BoxLayout({ vertical: true });
@ -346,6 +364,76 @@ const AllView = new Lang.Class({
this._keyPressEventId = 0;
}
}));
this._redisplayWorkId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
Shell.AppSystem.get_default().connect('installed-changed', Lang.bind(this, function() {
Main.queueDeferredWork(this._redisplayWorkId);
}));
this._folderSettings = new Gio.Settings({ schema: 'org.gnome.desktop.app-folders' });
this._folderSettings.connect('changed::folder-children', Lang.bind(this, function() {
Main.queueDeferredWork(this._redisplayWorkId);
}));
},
removeAll: function() {
this.folderIcons = [];
this.parent();
},
_itemNameChanged: function(item) {
// If an item's name changed, we can pluck it out of where it's
// supposed to be and reinsert it where it's sorted.
let oldIdx = this._allItems.indexOf(item);
this._allItems.splice(oldIdx, 1);
let newIdx = Util.insertSorted(this._allItems, item, this._compareItems);
this._grid.removeItem(item);
this._grid.addItem(item, newIdx);
},
_refilterApps: function() {
this._allItems.forEach(function(icon) {
if (icon instanceof AppIcon)
icon.actor.visible = true;
});
this.folderIcons.forEach(Lang.bind(this, function(folder) {
let folderApps = folder.getAppIds();
folderApps.forEach(Lang.bind(this, function(appId) {
let appIcon = this._items[appId];
appIcon.actor.visible = false;
}));
}));
},
_loadApps: function() {
let apps = Gio.AppInfo.get_all().filter(function(appInfo) {
return appInfo.should_show();
}).map(function(app) {
return app.get_id();
});
let appSys = Shell.AppSystem.get_default();
let folders = this._folderSettings.get_strv('folder-children');
folders.forEach(Lang.bind(this, function(id) {
let path = this._folderSettings.path + 'folders/' + id + '/';
let icon = new FolderIcon(id, path, this);
icon.connect('name-changed', Lang.bind(this, this._itemNameChanged));
icon.connect('apps-changed', Lang.bind(this, this._refilterApps));
this.addItem(icon);
this.folderIcons.push(icon);
}));
apps.forEach(Lang.bind(this, function(appId) {
let app = appSys.lookup_app(appId);
let icon = new AppIcon(app);
this.addItem(icon);
}));
this.loadGrid();
this._refilterApps();
},
getCurrentPageY: function() {
@ -353,8 +441,8 @@ const AllView = new Lang.Class({
},
goToPage: function(pageNumber) {
if(pageNumber < 0 || pageNumber > this._grid.nPages() - 1)
return;
pageNumber = clamp(pageNumber, 0, this._grid.nPages() - 1);
if (this._currentPage == pageNumber && this._displayingPopup && this._currentPopup)
return;
if (this._displayingPopup && this._currentPopup)
@ -386,14 +474,12 @@ const AllView = new Lang.Class({
// longer than PAGE_SWITCH_TIME
time = Math.min(time, PAGE_SWITCH_TIME);
if (pageNumber < this._grid.nPages() && pageNumber >= 0) {
this._currentPage = pageNumber;
Tweener.addTween(this._adjustment,
{ value: this._grid.getPageY(this._currentPage),
time: time,
transition: 'easeOutQuad' });
this._pageIndicators.setCurrentPage(pageNumber);
}
this._currentPage = pageNumber;
Tweener.addTween(this._adjustment,
{ value: this._grid.getPageY(this._currentPage),
time: time,
transition: 'easeOutQuad' });
this._pageIndicators.setCurrentPage(pageNumber);
},
_diffToPage: function (pageNumber) {
@ -415,7 +501,7 @@ const AllView = new Lang.Class({
_onScroll: function(actor, event) {
if (this._displayingPopup)
return true;
return Clutter.EVENT_STOP;
let direction = event.get_scroll_direction();
if (direction == Clutter.ScrollDirection.UP)
@ -423,7 +509,7 @@ const AllView = new Lang.Class({
else if (direction == Clutter.ScrollDirection.DOWN)
this.goToPage(this._currentPage + 1);
return true;
return Clutter.EVENT_STOP;
},
_onPan: function(action) {
@ -440,77 +526,35 @@ const AllView = new Lang.Class({
_onPanEnd: function(action) {
if (this._displayingPopup)
return;
let diffCurrentPage = this._diffToPage(this._currentPage);
if (diffCurrentPage > this._scrollView.height * PAGE_SWITCH_TRESHOLD) {
if (action.get_velocity(0)[2] > 0)
this.goToPage(this._currentPage - 1);
else
this.goToPage(this._currentPage + 1);
} else {
this.goToPage(this._currentPage);
}
let pageHeight = this._grid.getPageHeight();
// Calculate the scroll value we'd be at, which is our current
// scroll plus any velocity the user had when they released
// their finger.
let velocity = -action.get_velocity(0)[2];
let endPanValue = this._adjustment.value + velocity;
let closestPage = Math.round(endPanValue / pageHeight);
this.goToPage(closestPage);
this._panning = false;
},
_onKeyPressEvent: function(actor, event) {
if (this._displayingPopup)
return true;
return Clutter.EVENT_STOP;
if (event.get_key_symbol() == Clutter.Page_Up) {
this.goToPage(this._currentPage - 1);
return true;
return Clutter.EVENT_STOP;
} else if (event.get_key_symbol() == Clutter.Page_Down) {
this.goToPage(this._currentPage + 1);
return true;
return Clutter.EVENT_STOP;
}
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);
},
removeAll: function() {
this._folderIcons = [];
this.parent();
},
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);
this._folderIcons.push(folderIcon);
if (folderIcon)
folderIcon.actor.connect('key-focus-in',
Lang.bind(this, this._ensureIconVisible));
return Clutter.EVENT_PROPAGATE;
},
addFolderPopup: function(popup) {
@ -525,7 +569,7 @@ const AllView = new Lang.Class({
}));
},
_ensureIconVisible: function(icon) {
_keyFocusIn: function(icon) {
let itemPage = this._grid.getItemPage(icon);
this.goToPage(itemPage);
},
@ -577,8 +621,8 @@ const AllView = new Lang.Class({
this._availWidth = availWidth;
this._availHeight = availHeight;
// Update folder views
for (let i = 0; i < this._folderIcons.length; i++)
this._folderIcons[i].adaptToSize(availWidth, availHeight);
for (let i = 0; i < this.folderIcons.length; i++)
this.folderIcons[i].adaptToSize(availWidth, availHeight);
}
});
Signals.addSignalMethods(AllView.prototype);
@ -607,13 +651,18 @@ const FrequentView = new Lang.Class({
this._noFrequentAppsLabel.hide();
this._usage = Shell.AppUsage.get_default();
this.actor.connect('notify::mapped', Lang.bind(this, function() {
if (this.actor.mapped)
this._redisplay();
}));
},
hasUsefulData: function() {
return this._usage.get_most_used("").length >= MIN_FREQUENT_APPS_COUNT;
},
loadApps: function() {
_loadApps: function() {
let mostUsed = this._usage.get_most_used ("");
let hasUsefulData = this.hasUsefulData();
this._noFrequentAppsLabel.visible = !hasUsefulData;
@ -691,16 +740,6 @@ const AppDisplay = new Lang.Class({
Name: 'AppDisplay',
_init: function() {
this._appSystem = Shell.AppSystem.get_default();
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._privacySettings = new Gio.Settings({ schema: 'org.gnome.desktop.privacy' });
this._privacySettings.connect('changed::remember-app-usage',
Lang.bind(this, this._updateFrequentVisibility));
@ -729,7 +768,7 @@ const AppDisplay = new Lang.Class({
this._viewStack = new St.Widget({ x_expand: true, y_expand: true,
layout_manager: this._viewStackLayout });
this._viewStackLayout.connect('allocated-size-changed', Lang.bind(this, this._onAllocatedSizeChanged));
this.actor.add_actor(this._viewStack, { expand: true });
this.actor.add_actor(this._viewStack);
let layout = new ControlsBoxLayout({ homogeneous: true });
this._controls = new St.Widget({ style_class: 'app-view-controls',
layout_manager: layout });
@ -744,27 +783,25 @@ const AppDisplay = new Lang.Class({
this._views[i].control.connect('clicked', Lang.bind(this,
function(actor) {
this._showView(viewIndex);
global.settings.set_uint('app-picker-view', viewIndex);
}));
}
let initialView = Math.min(global.settings.get_uint('app-picker-view'),
this._views.length - 1);
let frequentUseful = this._views[Views.FREQUENT].view.hasUsefulData();
this._showView(frequentUseful ? Views.FREQUENT : Views.ALL);
if (initialView == Views.FREQUENT && !frequentUseful)
initialView = Views.ALL;
this._showView(initialView);
this._updateFrequentVisibility();
// 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._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));
},
_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 };
let params = { time: VIEWS_SWITCH_TIME,
opacity: (i == activeIndex) ? 255 : 0,
delay: (i == activeIndex) ? VIEWS_SWITCH_ANIMATION_DELAY : 0 };
if (i == activeIndex)
actor.visible = true;
else
@ -791,52 +828,6 @@ const AppDisplay = new Lang.Class({
this._showView(Views.ALL);
},
_redisplay: function() {
this._redisplayFrequentApps();
this._redisplayAllApps();
},
_redisplayFrequentApps: function() {
let view = this._views[Views.FREQUENT].view;
view.removeAll();
view.loadApps();
},
_redisplayAllApps: function() {
let view = this._views[Views.ALL].view;
view.removeAll();
let tree = this._appSystem.get_tree();
let root = tree.get_root_directory();
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) {
let dir = iter.get_directory();
if (dir.get_is_nodisplay())
continue;
if (folderCategories.indexOf(dir.get_menu_id()) != -1)
view.addFolder(dir);
else
_loadCategory(dir, view);
}
}
view.loadGrid();
if (this._focusDummy) {
let focused = this._focusDummy.has_key_focus();
this._focusDummy.destroy();
this._focusDummy = null;
if (focused)
this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
}
},
selectApp: function(id) {
this._showView(Views.ALL);
this._views[Views.ALL].view.selectApp(id);
@ -866,8 +857,8 @@ const AppSearchProvider = new Lang.Class({
getResultMetas: function(apps, callback) {
let metas = [];
for (let i = 0; i < apps.length; i++) {
let app = apps[i];
metas.push({ 'id': app,
let app = this._appSys.lookup_app(apps[i]);
metas.push({ 'id': app.get_id(),
'name': app.get_name(),
'createIcon': function(size) {
return app.create_icon_texture(size);
@ -877,15 +868,33 @@ const AppSearchProvider = new Lang.Class({
callback(metas);
},
getInitialResultSet: function(terms) {
this.searchSystem.setResults(this, this._appSys.initial_search(terms));
filterResults: function(results, maxNumber) {
return results.slice(0, maxNumber);
},
getSubsearchResultSet: function(previousResults, terms) {
this.searchSystem.setResults(this, this._appSys.subsearch(previousResults, terms));
getInitialResultSet: function(terms, callback, cancellable) {
let query = terms.join(' ');
let groups = Gio.DesktopAppInfo.search(query);
let usage = Shell.AppUsage.get_default();
let results = [];
groups.forEach(function(group) {
group = group.filter(function(appID) {
let app = Gio.DesktopAppInfo.new(appID);
return app && app.should_show();
});
results = results.concat(group.sort(function(a, b) {
return usage.compare('', a, b);
}));
});
callback(results);
},
activateResult: function(app) {
getSubsearchResultSet: function(previousResults, terms, callback, cancellable) {
this.getInitialResultSet(terms, callback, cancellable);
},
activateResult: function(result) {
let app = this._appSys.lookup_app(result);
let event = Clutter.get_current_event();
let modifiers = event ? event.get_state() : 0;
let openNewWindow = modifiers & Clutter.ModifierType.CONTROL_MASK;
@ -904,8 +913,8 @@ const AppSearchProvider = new Lang.Class({
app.open_new_window(workspace);
},
createResultObject: function (resultMeta, terms) {
let app = resultMeta['id'];
createResultObject: function (resultMeta) {
let app = this._appSys.lookup_app(resultMeta['id']);
return new AppIcon(app);
}
});
@ -931,36 +940,27 @@ const FolderView = new Lang.Class({
this.actor.add_action(action);
},
_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) {
this._addItem(app);
_keyFocusIn: function(actor) {
Util.ensureActorVisibleInScrollView(this.actor, actor);
},
createFolderIcon: function(size) {
let icon = new St.Widget({ layout_manager: new Clutter.BinLayout(),
style_class: 'app-folder-icon',
width: size, height: size });
let layout = new Clutter.TableLayout();
let icon = new St.Widget({ layout_manager: layout,
style_class: 'app-folder-icon' });
layout.hookup_style(icon);
let subSize = Math.floor(FOLDER_SUBICON_FRACTION * size);
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);
let numItems = this._allItems.length;
for (let i = 0; i < 4; i++) {
let bin;
if (i < numItems) {
let texture = this._allItems[i].app.create_icon_texture(subSize);
bin = new St.Bin({ child: texture });
} else {
bin = new St.Bin({ width: subSize, height: subSize });
}
layout.pack(bin, i % 2, Math.floor(i / 2));
}
return icon;
@ -1032,10 +1032,12 @@ const FolderView = new Lang.Class({
const FolderIcon = new Lang.Class({
Name: 'FolderIcon',
_init: function(dir, parentView) {
this._dir = dir;
_init: function(id, path, parentView) {
this.id = id;
this._parentView = parentView;
this._folder = new Gio.Settings({ schema_id: 'org.gnome.desktop.app-folders.folder',
path: path });
this.actor = new St.Button({ style_class: 'app-well-app app-folder',
button_mask: St.ButtonMask.ONE,
toggle_mode: true,
@ -1046,15 +1048,11 @@ const FolderIcon = new Lang.Class({
// whether we need to update arrow side, position etc.
this._popupInvalidated = false;
let label = this._dir.get_name();
this.icon = new IconGrid.BaseIcon(label,
{ createIcon: Lang.bind(this, this._createIcon), setSizeManually: true });
this.icon = new IconGrid.BaseIcon('', { createIcon: Lang.bind(this, this._createIcon), setSizeManually: true });
this.actor.set_child(this.icon.actor);
this.actor.label_actor = this.icon.label;
this.view = new FolderView();
_loadCategory(dir, this.view);
this.view.loadGrid();
this.actor.connect('clicked', Lang.bind(this,
function() {
@ -1067,6 +1065,63 @@ const FolderIcon = new Lang.Class({
if (!this.actor.mapped && this._popup)
this._popup.popdown();
}));
this._folder.connect('changed', Lang.bind(this, this._redisplay));
this._redisplay();
},
getAppIds: function() {
return this.view.getAllItems().map(function(item) {
return item.id;
});
},
_updateName: function() {
let name = _getFolderName(this._folder);
if (this.name == name)
return;
this.name = name;
this.icon.label.text = this.name;
this.emit('name-changed');
},
_redisplay: function() {
this._updateName();
this.view.removeAll();
let excludedApps = this._folder.get_strv('excluded-apps');
let appSys = Shell.AppSystem.get_default();
let addAppId = (function addAppId(appId) {
if (excludedApps.indexOf(appId) >= 0)
return;
let app = appSys.lookup_app(appId);
if (!app)
return;
if (!app.get_app_info().should_show())
return;
let icon = new AppIcon(app);
this.view.addItem(icon);
}).bind(this);
let folderApps = this._folder.get_strv('apps');
folderApps.forEach(addAppId);
let folderCategories = this._folder.get_strv('categories');
Gio.AppInfo.get_all().forEach(function(appInfo) {
let appCategories = _getCategories(appInfo);
if (!_listsIntersect(folderCategories, appCategories))
return;
addAppId(appInfo.get_id());
});
this.view.loadGrid();
this.emit('apps-changed');
},
_createIcon: function(iconSize) {
@ -1145,6 +1200,7 @@ const FolderIcon = new Lang.Class({
this._popupInvalidated = true;
},
});
Signals.addSignalMethods(FolderIcon.prototype);
const AppFolderPopup = new Lang.Class({
Name: 'AppFolderPopup',
@ -1198,13 +1254,13 @@ const AppFolderPopup = new Lang.Class({
_onKeyPress: function(actor, event) {
if (!this._isOpen)
return false;
return Clutter.EVENT_PROPAGATE;
if (event.get_key_symbol() != Clutter.KEY_Escape)
return false;
return Clutter.EVENT_PROPAGATE;
this.popdown();
return true;
return Clutter.EVENT_STOP;
},
toggle: function() {
@ -1263,6 +1319,9 @@ const AppIcon = new Lang.Class({
_init : function(app, iconParams) {
this.app = app;
this.id = app.get_id();
this.name = app.get_name();
this.actor = new St.Button({ style_class: 'app-well-app',
reactive: true,
button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO,
@ -1343,13 +1402,16 @@ const AppIcon = new Lang.Class({
this._removeMenuTimeout();
this._menuTimeoutId = Mainloop.timeout_add(MENU_POPUP_TIMEOUT,
Lang.bind(this, function() {
this._menuTimeoutId = 0;
this.popupMenu();
return GLib.SOURCE_REMOVE;
}));
GLib.Source.set_name_by_id(this._menuTimeoutId, '[gnome-shell] this.popupMenu');
} else if (button == 3) {
this.popupMenu();
return true;
return Clutter.EVENT_STOP;
}
return false;
return Clutter.EVENT_PROPAGATE;
},
_onClicked: function(actor, button) {
@ -1358,10 +1420,6 @@ const AppIcon = new Lang.Class({
if (button == 1) {
this._onActivate(Clutter.get_current_event());
} else if (button == 2) {
// Last workspace is always empty
let launchWorkspace = global.screen.get_workspace_by_index(global.screen.n_workspaces - 1);
launchWorkspace.activate(global.get_current_time());
this.emit('launching');
this.app.open_new_window(-1);
Main.overview.hide();
}
@ -1420,7 +1478,6 @@ const AppIcon = new Lang.Class({
},
_onActivate: function (event) {
this.emit('launching');
let modifiers = event.get_state();
if (modifiers & Clutter.ModifierType.CONTROL_MASK
@ -1472,8 +1529,6 @@ const AppIconMenu = new Lang.Class({
this._source = source;
this.connect('activate', Lang.bind(this, this._onActivate));
this.actor.add_style_class_name('app-well-menu');
// Chain our visibility and lifecycle to that of the source
@ -1489,7 +1544,9 @@ const AppIconMenu = new Lang.Class({
_redisplay: function() {
this.removeAll();
let windows = this._source.app.get_windows();
let windows = this._source.app.get_windows().filter(function(w) {
return !w.skip_taskbar;
});
// Display the app windows menu items and the separator between windows
// of the current desktop and other windows.
@ -1497,25 +1554,74 @@ const AppIconMenu = new Lang.Class({
let separatorShown = windows.length > 0 && windows[0].get_workspace() != activeWorkspace;
for (let i = 0; i < windows.length; i++) {
if (!separatorShown && windows[i].get_workspace() != activeWorkspace) {
let window = windows[i];
if (!separatorShown && window.get_workspace() != activeWorkspace) {
this._appendSeparator();
separatorShown = true;
}
let item = this._appendMenuItem(windows[i].title);
item._window = windows[i];
let item = this._appendMenuItem(window.title);
item.connect('activate', Lang.bind(this, function() {
this.emit('activate-window', window);
}));
}
if (!this._source.app.is_window_backed()) {
if (windows.length > 0)
this._appendSeparator();
this._appendSeparator();
this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
this._newWindowMenuItem.connect('activate', Lang.bind(this, function() {
this._source.app.open_new_window(-1);
this.emit('activate-window', null);
}));
this._appendSeparator();
let appInfo = this._source.app.get_app_info();
let actions = appInfo.list_actions();
for (let i = 0; i < actions.length; i++) {
let action = actions[i];
let item = this._appendMenuItem(appInfo.get_action_name(action));
item.connect('activate', Lang.bind(this, function(emitter, event) {
this._source.app.launch_action(action, event.get_time(), -1);
this.emit('activate-window', null);
}));
}
this._appendSeparator();
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
this._appendSeparator();
if (isFavorite) {
let item = this._appendMenuItem(_("Remove from Favorites"));
item.connect('activate', Lang.bind(this, function() {
let favs = AppFavorites.getAppFavorites();
favs.removeFavorite(this._source.app.get_id());
}));
} else {
let item = this._appendMenuItem(_("Add to Favorites"));
item.connect('activate', Lang.bind(this, function() {
let favs = AppFavorites.getAppFavorites();
favs.addFavorite(this._source.app.get_id());
}));
}
this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
: _("Add to Favorites"));
if (Shell.AppSystem.get_default().lookup_app('org.gnome.Software.desktop')) {
this._appendSeparator();
let item = this._appendMenuItem(_("Show Details"));
item.connect('activate', Lang.bind(this, function() {
let id = this._source.app.get_id();
let args = GLib.Variant.new('(ss)', [id, '']);
Gio.DBus.get(Gio.BusType.SESSION, null,
function(o, res) {
let bus = Gio.DBus.get_finish(res);
bus.call('org.gnome.Software',
'/org/gnome/Software',
'org.gtk.Actions', 'Activate',
GLib.Variant.new('(sava{sv})',
['details', [args], null]),
null, 0, -1, null, null);
Main.overview.hide();
});
}));
}
}
},
@ -1534,24 +1640,6 @@ const AppIconMenu = new Lang.Class({
popup: function(activatingButton) {
this._redisplay();
this.open();
},
_onActivate: function (actor, child) {
if (child._window) {
let metaWindow = child._window;
this.emit('activate-window', metaWindow);
} else if (child == this._newWindowMenuItem) {
this._source.app.open_new_window(-1);
this.emit('activate-window', null);
} else if (child == this._toggleFavoriteMenuItem) {
let favs = AppFavorites.getAppFavorites();
let isFavorite = favs.isFavorite(this._source.app.get_id());
if (isFavorite)
favs.removeFavorite(this._source.app.get_id());
else
favs.addFavorite(this._source.app.get_id());
}
this.close();
}
});
Signals.addSignalMethods(AppIconMenu.prototype);

View File

@ -50,11 +50,9 @@ const BackgroundCache = new Lang.Class({
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;
@ -88,7 +86,6 @@ const BackgroundCache = new Lang.Class({
}
this._patterns.push(content);
return content;
},
@ -116,9 +113,9 @@ const BackgroundCache = new Lang.Class({
_removeContent: function(contentList, content) {
let index = contentList.indexOf(content);
if (index >= 0)
contentList.splice(index, 1);
if (index < 0)
throw new Error("Trying to remove invalid content: " + content);
contentList.splice(index, 1);
},
removePatternContent: function(content) {
@ -128,12 +125,32 @@ const BackgroundCache = new Lang.Class({
removeImageContent: function(content) {
let filename = content.get_filename();
if (filename && this._fileMonitors[filename])
let hasOtherUsers = this._images.some(function(content) { return filename == content.get_filename(); });
if (!hasOtherUsers)
delete this._fileMonitors[filename];
this._removeContent(this._images, content);
},
_attachCallerToFileLoad: function(caller, fileLoad) {
fileLoad.callers.push(caller);
if (!caller.cancellable)
return;
caller.cancellable.connect(Lang.bind(this, function() {
let idx = fileLoad.callers.indexOf(caller);
fileLoad.callers.splice(idx, 1);
if (fileLoad.callers.length == 0) {
fileLoad.cancellable.cancel();
let idx = this._pendingFileLoads.indexOf(fileLoad);
this._pendingFileLoads.splice(idx, 1);
}
}));
},
_loadImageContent: function(params) {
params = Params.parse(params, { monitorIndex: 0,
style: null,
@ -142,27 +159,28 @@ const BackgroundCache = new Lang.Class({
cancellable: null,
onFinished: null });
let caller = { monitorIndex: params.monitorIndex,
effects: params.effects,
cancellable: params.cancellable,
onFinished: params.onFinished };
for (let i = 0; i < this._pendingFileLoads.length; i++) {
if (this._pendingFileLoads[i].filename == params.filename &&
this._pendingFileLoads[i].style == params.style) {
this._pendingFileLoads[i].callers.push({ shouldCopy: true,
monitorIndex: params.monitorIndex,
effects: params.effects,
onFinished: params.onFinished });
let fileLoad = this._pendingFileLoads[i];
if (fileLoad.filename == params.filename &&
fileLoad.style == params.style) {
this._attachCallerToFileLoad(caller, fileLoad);
return;
}
}
this._pendingFileLoads.push({ filename: params.filename,
style: params.style,
callers: [{ shouldCopy: false,
monitorIndex: params.monitorIndex,
effects: params.effects,
onFinished: params.onFinished }] });
let fileLoad = { filename: params.filename,
style: params.style,
cancellable: new Gio.Cancellable(),
callers: [] };
this._attachCallerToFileLoad(caller, fileLoad);
let content = new Meta.Background({ meta_screen: global.screen,
monitor: params.monitorIndex,
effects: params.effects });
let content = new Meta.Background({ meta_screen: global.screen });
content.load_file_async(params.filename,
params.style,
@ -173,31 +191,26 @@ const BackgroundCache = new Lang.Class({
content.load_file_finish(result);
this._monitorFile(params.filename);
this._images.push(content);
} catch(e) {
content = null;
}
for (let i = 0; i < this._pendingFileLoads.length; i++) {
let pendingLoad = this._pendingFileLoads[i];
if (pendingLoad.filename != params.filename ||
pendingLoad.style != params.style)
continue;
for (let i = 0; i < fileLoad.callers.length; i++) {
let caller = fileLoad.callers[i];
if (caller.onFinished) {
let newContent;
for (let j = 0; j < pendingLoad.callers.length; j++) {
if (pendingLoad.callers[j].onFinished) {
if (content && pendingLoad.callers[j].shouldCopy) {
content = object.copy(pendingLoad.callers[j].monitorIndex,
pendingLoad.callers[j].effects);
}
pendingLoad.callers[j].onFinished(content);
if (content) {
newContent = content.copy(caller.monitorIndex, caller.effects);
this._images.push(newContent);
}
}
this._pendingFileLoads.splice(i, 1);
caller.onFinished(newContent);
}
}
let idx = this._pendingFileLoads.indexOf(fileLoad);
this._pendingFileLoads.splice(idx, 1);
}));
},
@ -210,11 +223,9 @@ const BackgroundCache = new Lang.Class({
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;
@ -222,7 +233,7 @@ const BackgroundCache = new Lang.Class({
continue;
if (params.style == GDesktopEnums.BackgroundStyle.SPANNED &&
this._images[i].monitor_index != this._monitorIndex)
this._images[i].monitor != params.monitorIndex)
continue;
candidateContent = this._images[i];
@ -260,9 +271,11 @@ const BackgroundCache = new Lang.Class({
if (this._animationFilename == params.filename) {
if (params.onLoaded) {
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
params.onLoaded(this._animation);
return GLib.SOURCE_REMOVE;
}));
GLib.Source.set_name_by_id(id, '[gnome-shell] params.onLoaded');
}
}
@ -274,9 +287,11 @@ const BackgroundCache = new Lang.Class({
this._animation = animation;
if (params.onLoaded) {
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
params.onLoaded(this._animation);
return GLib.SOURCE_REMOVE;
}));
GLib.Source.set_name_by_id(id, '[gnome-shell] params.onLoaded');
}
}));
}
@ -315,13 +330,12 @@ const Background = new Lang.Class({
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._settingsChangedSignalId = this._settings.connect('changed', Lang.bind(this, function() {
this.emit('changed');
}));
this._load();
},
@ -362,6 +376,10 @@ const Background = new Lang.Class({
this.actor.disconnect(this._destroySignalId);
this._destroySignalId = 0;
if (this._settingsChangedSignalId != 0)
this._settings.disconnect(this._settingsChangedSignalId);
this._settingsChangedSignalId = 0;
},
_setLoaded: function() {
@ -370,10 +388,11 @@ const Background = new Lang.Class({
this.isLoaded = true;
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
this.emit('loaded');
return false;
return GLib.SOURCE_REMOVE;
}));
GLib.Source.set_name_by_id(id, '[gnome-shell] this.emit');
},
_loadPattern: function() {
@ -411,29 +430,26 @@ const Background = new Lang.Class({
this._fileWatches[filename] = signalId;
},
_addImage: function(content, index, filename) {
content.saturation = this._saturation;
content.brightness = this._brightness;
content.vignette_sharpness = this._vignetteSharpness;
_ensureImage: function(index) {
if (this._images[index])
return;
let actor = new Meta.BackgroundActor();
actor.content = content;
// The background pattern is the first actor in
// the group, and all images should be above that.
this.actor.insert_child_at_index(actor, index + 1);
this._images[index] = actor;
this._watchCacheFile(filename);
},
_updateImage: function(content, index, filename) {
content.saturation = this._saturation;
_updateImage: function(index, content, filename) {
content.brightness = this._brightness;
content.vignette_sharpness = this._vignetteSharpness;
this._cache.removeImageContent(this._images[index].content);
this._images[index].content = content;
let image = this._images[index];
if (image.content)
this._cache.removeImageContent(content);
image.content = content;
this._watchCacheFile(filename);
},
@ -481,11 +497,8 @@ const Background = new Lang.Class({
return;
}
if (!this._images[i]) {
this._addImage(content, i, files[i]);
} else {
this._updateImage(content, i, files[i]);
}
this._ensureImage(i);
this._updateImage(i, content, files[i]);
if (numPendingImages == 0) {
this._setLoaded();
@ -520,8 +533,9 @@ const Background = new Lang.Class({
Lang.bind(this, function() {
this._updateAnimationTimeoutId = 0;
this._updateAnimation();
return false;
return GLib.SOURCE_REMOVE;
}));
GLib.Source.set_name_by_id(this._updateAnimationTimeoutId, '[gnome-shell] this._updateAnimation');
},
_loadAnimation: function(filename) {
@ -540,30 +554,33 @@ const Background = new Lang.Class({
});
},
_loadFile: function(filename) {
_loadImage: 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;
if (content) {
this._ensureImage(0);
this._updateImage(0, content, filename);
}
this._addImage(content, 0, filename);
this._setLoaded();
})
});
},
_loadFile: function(filename) {
if (filename.endsWith('.xml'))
this._loadAnimation(filename);
else
this._loadImage(filename);
},
_load: function () {
this._cache = getBackgroundCache();
this._loadPattern(this._cache);
this._loadPattern();
this._style = this._settings.get_enum(BACKGROUND_STYLE_KEY);
if (this._style == GDesktopEnums.BackgroundStyle.NONE) {
@ -586,24 +603,6 @@ const Background = new Lang.Class({
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;
},
@ -655,7 +654,13 @@ const SystemBackground = new Lang.Class({
this.emit('loaded');
})
});
}
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
},
_onDestroy: function() {
this._cache.removeImageContent(this.actor.content);
},
});
Signals.addSignalMethods(SystemBackground.prototype);
@ -743,30 +748,31 @@ const BackgroundManager = new Lang.Class({
}
},
_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;
_updateBackground: function() {
let newBackground = this._createBackground();
newBackground.vignetteSharpness = this.background.vignetteSharpness;
newBackground.brightness = this.background.brightness;
newBackground.visible = this.background.visible;
newBackground.loadedSignalId = newBackground.connect('loaded',
Lang.bind(this, function() {
newBackground.disconnect(newBackground.loadedSignalId);
newBackground.loadedSignalId = 0;
Tweener.addTween(background.actor,
if (this._newBackground != newBackground) {
/* Not interesting, we queued another load */
newBackground.actor.destroy();
return;
}
Tweener.addTween(this.background.actor,
{ opacity: 0,
time: FADE_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
if (this._newBackground == newBackground) {
this.background = newBackground;
this._newBackground = null;
} else {
newBackground.actor.destroy();
}
background.actor.destroy();
this.background.actor.destroy();
this.background = newBackground;
this._newBackground = null;
this.emit('changed');
})
@ -794,7 +800,7 @@ const BackgroundManager = new Lang.Class({
background.changeSignalId = background.connect('changed', Lang.bind(this, function() {
background.disconnect(background.changeSignalId);
background.changeSignalId = 0;
this._updateBackground(background, this._monitorIndex);
this._updateBackground();
}));
background.actor.connect('destroy', Lang.bind(this, function() {

View File

@ -13,8 +13,8 @@ const BackgroundMenu = new Lang.Class({
Name: 'BackgroundMenu',
Extends: PopupMenu.PopupMenu,
_init: function(source) {
this.parent(source, 0, St.Side.TOP);
_init: function(layoutManager) {
this.parent(layoutManager.dummyCursor, 0, St.Side.TOP);
this.addSettingsAction(_("Settings"), 'gnome-control-center.desktop');
this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
@ -22,23 +22,20 @@ const BackgroundMenu = new Lang.Class({
this.actor.add_style_class_name('background-menu');
Main.uiGroup.add_actor(this.actor);
layoutManager.uiGroup.add_actor(this.actor);
this.actor.hide();
}
});
function addBackgroundMenu(actor) {
let cursor = new St.Bin({ opacity: 0 });
Main.uiGroup.add_actor(cursor);
function addBackgroundMenu(actor, layoutManager) {
actor.reactive = true;
actor._backgroundMenu = new BackgroundMenu(cursor);
actor._backgroundMenu = new BackgroundMenu(layoutManager);
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);
Main.layoutManager.setDummyCursorGeometry(x, y, 0, 0);
actor._backgroundMenu.open(BoxPointer.PopupAnimation.NONE);
}
@ -59,10 +56,8 @@ function addBackgroundMenu(actor) {
actor.add_action(clickAction);
actor.connect('destroy', function() {
actor._backgroundMenu.destroy();
actor._backgroundMenu = null;
actor._backgroundManager = null;
cursor.destroy();
});
actor._backgroundMenu.destroy();
actor._backgroundMenu = null;
actor._backgroundManager = null;
});
}

View File

@ -69,7 +69,7 @@ const BoxPointer = new Lang.Class({
_muteInput: function() {
if (this._capturedEventId == 0)
this._capturedEventId = this.actor.connect('captured-event',
function() { return true; });
function() { return Clutter.EVENT_STOP; });
},
_unmuteInput: function() {
@ -121,6 +121,9 @@ const BoxPointer = new Lang.Class({
},
hide: function(animate, onComplete) {
if (!this.actor.visible)
return;
let xOffset = 0;
let yOffset = 0;
let themeNode = this.actor.get_theme_node();
@ -185,7 +188,9 @@ const BoxPointer = new Lang.Class({
},
_getPreferredHeight: function(actor, forWidth, alloc) {
let [minSize, naturalSize] = this.bin.get_preferred_height(forWidth);
let themeNode = this.actor.get_theme_node();
let borderWidth = themeNode.get_length('-arrow-border-width');
let [minSize, naturalSize] = this.bin.get_preferred_height(forWidth - 2 * borderWidth);
alloc.min_size = minSize;
alloc.natural_size = naturalSize;
this._adjustAllocationForArrow(false, alloc);
@ -282,38 +287,40 @@ const BoxPointer = new Lang.Class({
let skipBottomLeft = false;
let skipBottomRight = false;
switch (this._arrowSide) {
case St.Side.TOP:
if (this._arrowOrigin == x1)
skipTopLeft = true;
else if (this._arrowOrigin == x2)
skipTopRight = true;
break;
if (rise) {
switch (this._arrowSide) {
case St.Side.TOP:
if (this._arrowOrigin == x1)
skipTopLeft = true;
else if (this._arrowOrigin == x2)
skipTopRight = true;
break;
case St.Side.RIGHT:
if (this._arrowOrigin == y1)
skipTopRight = true;
else if (this._arrowOrigin == y2)
skipBottomRight = true;
break;
case St.Side.RIGHT:
if (this._arrowOrigin == y1)
skipTopRight = true;
else if (this._arrowOrigin == y2)
skipBottomRight = true;
break;
case St.Side.BOTTOM:
if (this._arrowOrigin == x1)
skipBottomLeft = true;
else if (this._arrowOrigin == x2)
skipBottomRight = true;
break;
case St.Side.BOTTOM:
if (this._arrowOrigin == x1)
skipBottomLeft = true;
else if (this._arrowOrigin == x2)
skipBottomRight = true;
break;
case St.Side.LEFT:
if (this._arrowOrigin == y1)
skipTopLeft = true;
else if (this._arrowOrigin == y2)
skipBottomLeft = true;
break;
case St.Side.LEFT:
if (this._arrowOrigin == y1)
skipTopLeft = true;
else if (this._arrowOrigin == y2)
skipBottomLeft = true;
break;
}
}
cr.moveTo(x1 + borderRadius, y1);
if (this._arrowSide == St.Side.TOP) {
if (this._arrowSide == St.Side.TOP && rise) {
if (skipTopLeft) {
cr.moveTo(x1, y2 - borderRadius);
cr.lineTo(x1, y1 - rise);
@ -335,7 +342,7 @@ const BoxPointer = new Lang.Class({
3*Math.PI/2, Math.PI*2);
}
if (this._arrowSide == St.Side.RIGHT) {
if (this._arrowSide == St.Side.RIGHT && rise) {
if (skipTopRight) {
cr.lineTo(x2 + rise, y1);
cr.lineTo(x2 + rise, y1 + halfBase);
@ -356,7 +363,7 @@ const BoxPointer = new Lang.Class({
0, Math.PI/2);
}
if (this._arrowSide == St.Side.BOTTOM) {
if (this._arrowSide == St.Side.BOTTOM && rise) {
if (skipBottomLeft) {
cr.lineTo(x1 + halfBase, y2);
cr.lineTo(x1, y2 + rise);
@ -377,7 +384,7 @@ const BoxPointer = new Lang.Class({
Math.PI/2, Math.PI);
}
if (this._arrowSide == St.Side.LEFT) {
if (this._arrowSide == St.Side.LEFT && rise) {
if (skipTopLeft) {
cr.lineTo(x1, y1 + halfBase);
cr.lineTo(x1 - rise, y1);

View File

@ -14,19 +14,24 @@ const Shell = imports.gi.Shell;
const MSECS_IN_DAY = 24 * 60 * 60 * 1000;
const SHOW_WEEKDATE_KEY = 'show-weekdate';
// alias to prevent xgettext from picking up strings translated in GTK+
const gtk30_ = Gettext_gtk30.gettext;
// in org.gnome.desktop.interface
const CLOCK_FORMAT_KEY = 'clock-format';
function _sameDay(dateA, dateB) {
return (dateA.getDate() == dateB.getDate() &&
dateA.getMonth() == dateB.getMonth() &&
dateA.getYear() == dateB.getYear());
}
function _sameYear(dateA, dateB) {
return (dateA.getYear() == dateB.getYear());
}
function _sameMonth(dateA, dateB) {
return _sameYear(dateA, dateB) && (dateA.getMonth() == dateB.getMonth());
}
function _sameDay(dateA, dateB) {
return _sameMonth(dateA, dateB) && (dateA.getDate() == dateB.getDate());
}
/* TODO: maybe needs config - right now we assume that Saturday and
* Sunday are non-work days (not true in e.g. Israel, it's Sunday and
* Monday there)
@ -190,16 +195,18 @@ const EmptyEventSource = new Lang.Class({
});
Signals.addSignalMethods(EmptyEventSource.prototype);
const CalendarServerIface = <interface name="org.gnome.Shell.CalendarServer">
<method name="GetEvents">
<arg type="x" direction="in" />
<arg type="x" direction="in" />
<arg type="b" direction="in" />
<arg type="a(sssbxxa{sv})" direction="out" />
</method>
<property name="HasCalendars" type="b" access="read" />
<signal name="Changed" />
</interface>;
const CalendarServerIface = '<node> \
<interface name="org.gnome.Shell.CalendarServer"> \
<method name="GetEvents"> \
<arg type="x" direction="in" /> \
<arg type="x" direction="in" /> \
<arg type="b" direction="in" /> \
<arg type="a(sssbxxa{sv})" direction="out" /> \
</method> \
<property name="HasCalendars" type="b" access="read" /> \
<signal name="Changed" /> \
</interface> \
</node>';
const CalendarServerInfo = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIface);
@ -327,25 +334,22 @@ const DBusEventSource = new Lang.Class({
return;
if (this._curRequestBegin && this._curRequestEnd){
let callFlags = Gio.DBusCallFlags.NO_AUTO_START;
if (forceReload)
callFlags = Gio.DBusCallFlags.NONE;
this._dbusProxy.GetEventsRemote(this._curRequestBegin.getTime() / 1000,
this._curRequestEnd.getTime() / 1000,
forceReload,
Lang.bind(this, this._onEventsReceived),
callFlags);
Gio.DBusCallFlags.NONE);
}
},
requestRange: function(begin, end, forceReload) {
if (forceReload || !(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
requestRange: function(begin, end) {
if (!(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) {
this.isLoading = true;
this._lastRequestBegin = begin;
this._lastRequestEnd = end;
this._curRequestBegin = begin;
this._curRequestEnd = end;
this._loadEvents(forceReload);
this._loadEvents(false);
}
},
@ -386,7 +390,7 @@ const Calendar = new Lang.Class({
// Find the ordering for month/year in the calendar heading
this._headerFormatWithoutYear = '%B';
switch (Gettext_gtk30.gettext('calendar:MY')) {
switch (gtk30_('calendar:MY')) {
case 'calendar:MY':
this._headerFormat = '%B %Y';
break;
@ -402,9 +406,11 @@ const Calendar = new Lang.Class({
// Start off with the current date
this._selectedDate = new Date();
this.actor = new St.Table({ homogeneous: false,
style_class: 'calendar',
reactive: true });
this._shouldDateGrabFocus = false;
this.actor = new St.Widget({ style_class: 'calendar',
layout_manager: new Clutter.TableLayout(),
reactive: true });
this.actor.connect('scroll-event',
Lang.bind(this, this._onScroll));
@ -417,31 +423,32 @@ const Calendar = new Lang.Class({
setEventSource: function(eventSource) {
this._eventSource = eventSource;
this._eventSource.connect('changed', Lang.bind(this, function() {
this._update(false);
this._rebuildCalendar();
this._update();
}));
this._update(true);
this._rebuildCalendar();
this._update();
},
// Sets the calendar to show a specific date
setDate: function(date, forceReload) {
if (!_sameDay(date, this._selectedDate)) {
this._selectedDate = date;
this._update(forceReload);
this.emit('selected-date-changed', new Date(this._selectedDate));
} else {
if (forceReload)
this._update(forceReload);
}
setDate: function(date) {
if (_sameDay(date, this._selectedDate))
return;
this._selectedDate = date;
this._update();
this.emit('selected-date-changed', new Date(this._selectedDate));
},
_buildHeader: function() {
let layout = this.actor.layout_manager;
let offsetCols = this._useWeekdate ? 1 : 0;
this.actor.destroy_all_children();
// Top line of the calendar '<| September 2009 |>'
this._topBox = new St.BoxLayout();
this.actor.add(this._topBox,
{ row: 0, col: 0, col_span: offsetCols + 7 });
layout.pack(this._topBox, 0, 0);
layout.set_span(this._topBox, offsetCols + 7, 1);
this._backButton = new St.Button({ style_class: 'calendar-change-month-back',
accessible_name: _("Previous month"),
@ -473,10 +480,8 @@ const Calendar = new Lang.Class({
let customDayAbbrev = _getCalendarDayAbbreviation(iter.getDay());
let label = new St.Label({ style_class: 'calendar-day-base calendar-day-heading',
text: customDayAbbrev });
this.actor.add(label,
{ row: 1,
col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7,
x_fill: false, x_align: St.Align.MIDDLE });
let col = offsetCols + (7 + iter.getDay() - this._weekStart) % 7;
layout.pack(label, col, 1);
iter.setTime(iter.getTime() + MSECS_IN_DAY);
}
@ -495,6 +500,7 @@ const Calendar = new Lang.Class({
this._onNextMonthButtonClicked();
break;
}
return Clutter.EVENT_PROPAGATE;
},
_onPrevMonthButtonClicked: function() {
@ -518,7 +524,7 @@ const Calendar = new Lang.Class({
this._backButton.grab_key_focus();
this.setDate(newDate, false);
this.setDate(newDate);
},
_onNextMonthButtonClicked: function() {
@ -542,28 +548,26 @@ const Calendar = new Lang.Class({
this._forwardButton.grab_key_focus();
this.setDate(newDate, false);
this.setDate(newDate);
},
_onSettingsChange: function() {
this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
this._buildHeader();
this._update(false);
this._rebuildCalendar();
this._update();
},
_update: function(forceReload) {
_rebuildCalendar: function() {
let now = new Date();
if (_sameYear(this._selectedDate, now))
this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormatWithoutYear);
else
this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormat);
// Remove everything but the topBox and the weekday labels
let children = this.actor.get_children();
for (let i = this._firstDayIndex; i < children.length; i++)
children[i].destroy();
this._buttons = [];
// Start at the beginning of the week before the start of the month
//
// We want to show always 6 weeks (to keep the calendar menu at the same
@ -581,11 +585,13 @@ const Calendar = new Lang.Class({
// Actually computing the number of weeks is complex, but we know that the
// problematic categories (2 and 4) always start on week start, and that
// all months at the end have 6 weeks.
let beginDate = new Date(this._selectedDate);
beginDate.setDate(1);
beginDate.setSeconds(0);
beginDate.setHours(12);
this._calendarBegin = new Date(beginDate);
let year = beginDate.getYear();
let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7;
@ -594,6 +600,7 @@ const Calendar = new Lang.Class({
beginDate.setTime(beginDate.getTime() - (weekPadding + daysToWeekStart) * MSECS_IN_DAY);
let layout = this.actor.layout_manager;
let iter = new Date(beginDate);
let row = 2;
// nRows here means 6 weeks + one header + one navbar
@ -606,13 +613,10 @@ const Calendar = new Lang.Class({
if (this._eventSource.isDummy)
button.reactive = false;
let iterStr = iter.toUTCString();
button._date = new Date(iter);
button.connect('clicked', Lang.bind(this, function() {
this._shouldDateGrabFocus = true;
let newlySelectedDate = new Date(iterStr);
this.setDate(newlySelectedDate, false);
this.setDate(button._date);
this._shouldDateGrabFocus = false;
}));
@ -620,9 +624,9 @@ const Calendar = new Lang.Class({
let styleClass = 'calendar-day-base calendar-day';
if (_isWorkDay(iter))
styleClass += ' calendar-work-day'
styleClass += ' calendar-work-day';
else
styleClass += ' calendar-nonwork-day'
styleClass += ' calendar-nonwork-day';
// Hack used in lieu of border-collapse - see gnome-shell.css
if (row == 2)
@ -639,26 +643,20 @@ const Calendar = new Lang.Class({
styleClass += ' calendar-other-month-day';
if (hasEvents)
styleClass += ' calendar-day-with-events'
styleClass += ' calendar-day-with-events';
button.style_class = styleClass;
let offsetCols = this._useWeekdate ? 1 : 0;
this.actor.add(button,
{ row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 });
let col = offsetCols + (7 + iter.getDay() - this._weekStart) % 7;
layout.pack(button, col, row);
if (_sameDay(this._selectedDate, iter)) {
button.add_style_pseudo_class('active');
if (this._shouldDateGrabFocus)
button.grab_key_focus();
}
this._buttons.push(button);
if (this._useWeekdate && iter.getDay() == 4) {
let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(),
style_class: 'calendar-day-base calendar-week-number'});
this.actor.add(label,
{ row: row, col: 0, y_align: St.Align.MIDDLE });
layout.pack(label, 0, row);
}
iter.setTime(iter.getTime() + MSECS_IN_DAY);
@ -666,9 +664,32 @@ const Calendar = new Lang.Class({
if (iter.getDay() == this._weekStart)
row++;
}
// Signal to the event source that we are interested in events
// only from this date range
this._eventSource.requestRange(beginDate, iter, forceReload);
this._eventSource.requestRange(beginDate, iter);
},
_update: function() {
let now = new Date();
if (_sameYear(this._selectedDate, now))
this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormatWithoutYear);
else
this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormat);
if (!this._calendarBegin || !_sameMonth(this._selectedDate, this._calendarBegin))
this._rebuildCalendar();
this._buttons.forEach(Lang.bind(this, function(button) {
if (_sameDay(button._date, this._selectedDate)) {
button.add_style_pseudo_class('active');
if (this._shouldDateGrabFocus)
button.grab_key_focus();
}
else
button.remove_style_pseudo_class('active');
}));
}
});
@ -678,7 +699,10 @@ const EventsList = new Lang.Class({
Name: 'EventsList',
_init: function() {
this.actor = new St.Table({ style_class: 'events-table' });
let layout = new Clutter.TableLayout();
this.actor = new St.Widget({ style_class: 'events-table',
layout_manager: layout });
layout.hookup_style(this.actor);
this._date = new Date();
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
@ -702,9 +726,11 @@ const EventsList = new Lang.Class({
dayLabel.clutter_text.line_wrap = false;
dayLabel.clutter_text.ellipsize = false;
this.actor.add(dayLabel, { row: index, col: 0,
x_expand: false, x_align: St.Align.END,
y_fill: false, y_align: St.Align.START });
let layout = this.actor.layout_manager;
layout.pack(dayLabel, 0, index);
layout.child_set(dayLabel, { x_expand: false,
x_align: Clutter.TableAlignment.END,
y_align: Clutter.TableAlignment.START });
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);
let timeString = _formatEventTime(event, clockFormat);
@ -713,18 +739,17 @@ const EventsList = new Lang.Class({
timeLabel.clutter_text.line_wrap = false;
timeLabel.clutter_text.ellipsize = false;
this.actor.add(timeLabel, { row: index, col: 1,
x_expand: false, x_align: St.Align.MIDDLE,
y_fill: false, y_align: St.Align.START });
layout.pack(timeLabel, 1, index);
layout.child_set(timeLabel, { x_expand: false,
y_align: Clutter.TableAlignment.START });
let titleLabel = new St.Label({ style_class: 'events-day-task',
text: event.summary });
titleLabel.clutter_text.line_wrap = true;
titleLabel.clutter_text.ellipsize = false;
this.actor.add(titleLabel, { row: index, col: 2,
x_expand: true, x_align: St.Align.START,
y_fill: false, y_align: St.Align.START });
layout.pack(titleLabel, 2, index);
layout.child_set(titleLabel, { x_expand: true });
},
_addPeriod: function(header, index, begin, end, includeDayName, showNothingScheduled) {
@ -733,13 +758,10 @@ const EventsList = new Lang.Class({
if (events.length == 0 && !showNothingScheduled)
return index;
this.actor.add(new St.Label({ style_class: 'events-day-header', text: header }),
{ row: index, col: 0, col_span: 3,
// In theory, x_expand should be true here, but x_expand
// is a property of the column for StTable, ie all day cells
// get it too
x_expand: false, x_align: St.Align.START,
y_fill: false, y_align: St.Align.START });
let label = new St.Label({ style_class: 'events-day-header', text: header });
let layout = this.actor.layout_manager;
layout.pack(label, 0, index);
layout.child_set(label, { column_span: 3, x_expand: false });
index++;
for (let n = 0; n < events.length; n++) {

View File

@ -43,6 +43,7 @@ const AutomountManager = new Lang.Class({
this._driveEjectButtonId = this._volumeMonitor.connect('drive-eject-button', Lang.bind(this, this._onDriveEjectButton));
this._mountAllId = Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
GLib.Source.set_name_by_id(this._mountAllId, '[gnome-shell] this._startupMountAll');
},
disable: function() {
@ -77,7 +78,7 @@ const AutomountManager = new Lang.Class({
}));
this._mountAllId = 0;
return false;
return GLib.SOURCE_REMOVE;
},
_onDriveConnected: function() {
@ -234,10 +235,11 @@ const AutomountManager = new Lang.Class({
},
_allowAutorunExpire: function(volume) {
Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() {
let id = Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() {
volume.allowAutorun = false;
return false;
return GLib.SOURCE_REMOVE;
});
GLib.Source.set_name_by_id(id, '[gnome-shell] volume.allowAutorun');
}
});
const Component = AutomountManager;

View File

@ -64,7 +64,7 @@ function startAppForMount(app, mount) {
try {
retval = app.launch(files,
global.create_app_launch_context())
global.create_app_launch_context(0, -1))
} catch (e) {
log('Unable to launch the application ' + app.get_name()
+ ': ' + e.toString());
@ -75,12 +75,14 @@ function startAppForMount(app, mount) {
/******************************************/
const HotplugSnifferIface = <interface name="org.gnome.Shell.HotplugSniffer">
<method name="SniffURI">
<arg type="s" direction="in" />
<arg type="as" direction="out" />
</method>
</interface>;
const HotplugSnifferIface = '<node> \
<interface name="org.gnome.Shell.HotplugSniffer"> \
<method name="SniffURI"> \
<arg type="s" direction="in" /> \
<arg type="as" direction="out" /> \
</method> \
</interface> \
</node>';
const HotplugSnifferProxy = Gio.DBusProxy.makeProxyWrapper(HotplugSnifferIface);
function HotplugSniffer() {
@ -168,17 +170,6 @@ const AutorunManager = new Lang.Class({
this._transDispatcher = new AutorunTransientDispatcher(this);
},
_ensureResidentSource: function() {
if (this._residentSource)
return;
this._residentSource = new AutorunResidentSource(this);
let destroyId = this._residentSource.connect('destroy', Lang.bind(this, function() {
this._residentSource.disconnect(destroyId);
this._residentSource = null;
}));
},
enable: function() {
this._scanMounts();
@ -187,17 +178,12 @@ const AutorunManager = new Lang.Class({
},
disable: function() {
if (this._residentSource)
this._residentSource.destroy();
this._volumeMonitor.disconnect(this._mountAddedId);
this._volumeMonitor.disconnect(this._mountRemovedId);
},
_processMount: function(mount, hotplug) {
let discoverer = new ContentTypeDiscoverer(Lang.bind(this, function(mount, apps, contentTypes) {
this._ensureResidentSource();
this._residentSource.addMount(mount, apps);
if (hotplug)
this._transDispatcher.addMount(mount, apps, contentTypes);
}));
@ -222,8 +208,6 @@ const AutorunManager = new Lang.Class({
_onMountRemoved: function(monitor, mount) {
this._transDispatcher.removeMount(mount);
if (this._residentSource)
this._residentSource.removeMount(mount);
},
ejectMount: function(mount) {
@ -286,153 +270,6 @@ const AutorunManager = new Lang.Class({
},
});
const AutorunResidentSource = new Lang.Class({
Name: 'AutorunResidentSource',
Extends: MessageTray.Source,
_init: function(manager) {
this.parent(_("Removable Devices"), 'media-removable');
this.resident = true;
this._mounts = [];
this._manager = manager;
this._notification = new AutorunResidentNotification(this._manager, this);
},
_createPolicy: function() {
return new MessageTray.NotificationPolicy({ showInLockScreen: false });
},
buildRightClickMenu: function() {
return null;
},
addMount: function(mount, apps) {
if (!shouldAutorunMount(mount, false))
return;
let filtered = this._mounts.filter(function (element) {
return (element.mount == mount);
});
if (filtered.length != 0)
return;
let element = { mount: mount, apps: apps };
this._mounts.push(element);
this._redisplay();
},
removeMount: function(mount) {
this._mounts =
this._mounts.filter(function (element) {
return (element.mount != mount);
});
this._redisplay();
},
_redisplay: function() {
if (this._mounts.length == 0) {
this._notification.destroy();
this.destroy();
return;
}
this._notification.updateForMounts(this._mounts);
// add ourselves as a source, and push the notification
if (!Main.messageTray.contains(this)) {
Main.messageTray.add(this);
this.pushNotification(this._notification);
}
}
});
const AutorunResidentNotification = new Lang.Class({
Name: 'AutorunResidentNotification',
Extends: MessageTray.Notification,
_init: function(manager, source) {
this.parent(source, source.title, null, { customContent: true });
// set the notification as resident
this.setResident(true);
this._layout = new St.BoxLayout ({ style_class: 'hotplug-resident-box',
vertical: true });
this._manager = manager;
this.addActor(this._layout,
{ x_expand: true,
x_fill: true });
},
updateForMounts: function(mounts) {
// remove all the layout content
this._layout.destroy_all_children();
for (let idx = 0; idx < mounts.length; idx++) {
let element = mounts[idx];
let actor = this._itemForMount(element.mount, element.apps);
this._layout.add(actor, { x_fill: true,
expand: true });
}
},
_itemForMount: function(mount, apps) {
let item = new St.BoxLayout();
// prepare the mount button content
let mountLayout = new St.BoxLayout();
let mountIcon = new St.Icon({ gicon: mount.get_icon(),
style_class: 'hotplug-resident-mount-icon' });
mountLayout.add_actor(mountIcon);
let labelBin = new St.Bin({ y_align: St.Align.MIDDLE });
let mountLabel =
new St.Label({ text: mount.get_name(),
style_class: 'hotplug-resident-mount-label',
track_hover: true,
reactive: true });
labelBin.add_actor(mountLabel);
mountLayout.add_actor(labelBin);
let mountButton = new St.Button({ child: mountLayout,
x_align: St.Align.START,
x_fill: true,
style_class: 'hotplug-resident-mount',
button_mask: St.ButtonMask.ONE });
item.add(mountButton, { x_align: St.Align.START,
expand: true });
let ejectIcon =
new St.Icon({ icon_name: 'media-eject-symbolic',
style_class: 'hotplug-resident-eject-icon' });
let ejectButton =
new St.Button({ style_class: 'hotplug-resident-eject-button',
button_mask: St.ButtonMask.ONE,
child: ejectIcon });
item.add(ejectButton, { x_align: St.Align.END });
// now connect signals
mountButton.connect('clicked', Lang.bind(this, function(actor, event) {
startAppForMount(apps[0], mount);
}));
ejectButton.connect('clicked', Lang.bind(this, function() {
this._manager.ejectMount(mount);
}));
return item;
},
});
const AutorunTransientDispatcher = new Lang.Class({
Name: 'AutorunTransientDispatcher',
@ -557,12 +394,12 @@ const AutorunTransientNotification = new Lang.Class({
Extends: MessageTray.Notification,
_init: function(manager, source) {
this.parent(source, source.title, null, { customContent: true });
this.parent(source, source.title);
this._manager = manager;
this._box = new St.BoxLayout({ style_class: 'hotplug-transient-box',
vertical: true });
this.addActor(this._box);
this._bodyBin.child = this._box;
this._mount = source.mount;

View File

@ -13,8 +13,6 @@ const ModalDialog = imports.ui.modalDialog;
const ShellEntry = imports.ui.shellEntry;
const CheckBox = imports.ui.checkBox;
let prompter = null;
const KeyringDialog = new Lang.Class({
Name: 'KeyringDialog',
Extends: ModalDialog.ModalDialog,
@ -47,7 +45,9 @@ const KeyringDialog = new Lang.Class({
this.prompt.bind_property('message', subject, 'text', GObject.BindingFlags.SYNC_CREATE);
this._messageBox.add(subject,
{ y_fill: false,
{ x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
let description = new St.Label({ style_class: 'prompt-dialog-description' });
@ -138,6 +138,7 @@ const KeyringDialog = new Lang.Class({
warning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
warning.clutter_text.line_wrap = true;
layout.pack(warning, 1, row);
layout.child_set(warning, { x_fill: false, x_align: Clutter.TableAlignment.START });
this.prompt.bind_property('warning-visible', warning, 'visible', GObject.BindingFlags.SYNC_CREATE);
this.prompt.bind_property('warning', warning, 'text', GObject.BindingFlags.SYNC_CREATE);
@ -248,11 +249,13 @@ const KeyringPrompter = new Lang.Class({
function() {
let dialog = this._enabled ? new KeyringDialog()
: new KeyringDummyDialog();
return dialog.prompt;
this._currentPrompt = dialog.prompt;
return this._currentPrompt;
}));
this._dbusId = null;
this._registered = false;
this._enabled = false;
this._currentPrompt = null;
},
enable: function() {
@ -267,6 +270,10 @@ const KeyringPrompter = new Lang.Class({
disable: function() {
this._enabled = false;
if (this._prompter.prompting)
this._currentPrompt.cancel();
this._currentPrompt = null;
}
});

View File

@ -138,6 +138,8 @@ const NetworkSecretDialog = new Lang.Class({
key: Clutter.KEY_Escape,
},
this._okButton]);
this._updateOkButton();
},
_updateOkButton: function() {
@ -253,6 +255,7 @@ const NetworkSecretDialog = new Lang.Class({
case 'leap':
case 'ttls':
case 'peap':
case 'fast':
// TTLS and PEAP are actually much more complicated, but this complication
// is not visible here since we only care about phase2 authentication
// (and don't even care of which one)
@ -306,7 +309,7 @@ const NetworkSecretDialog = new Lang.Class({
wirelessSetting = this._connection.get_setting_wireless();
ssid = NetworkManager.utils_ssid_to_utf8(wirelessSetting.get_ssid());
content.title = _("Authentication required by wireless network");
content.message = _("Passwords or encryption keys are required to access the wireless network '%s'.").format(ssid);
content.message = _("Passwords or encryption keys are required to access the wireless network %s.").format(ssid);
this._getWirelessSecrets(content.secrets, wirelessSetting);
break;
case '802-3-ethernet':
@ -333,7 +336,7 @@ const NetworkSecretDialog = new Lang.Class({
case 'cdma':
case 'bluetooth':
content.title = _("Mobile broadband network password");
content.message = _("A password is required to connect to '%s'.").format(connectionSetting.get_id());
content.message = _("A password is required to connect to %s.").format(connectionSetting.get_id());
this._getMobileSecrets(content.secrets, connectionType);
break;
default:
@ -432,6 +435,7 @@ const VPNRequestHandler = new Lang.Class({
},
_vpnChildFinished: function(pid, status, requestObj) {
this._childWatch = 0;
if (this._newStylePlugin) {
// For new style plugin, all work is done in the async reading functions
// Just reap the process here
@ -506,10 +510,12 @@ const VPNRequestHandler = new Lang.Class({
_showNewStyleDialog: function() {
let keyfile = new GLib.KeyFile();
let data;
let contentOverride;
try {
let data = this._dataStdout.peek_buffer();
data = this._dataStdout.peek_buffer();
keyfile.load_from_data(data.toString(), data.length,
GLib.KeyFileFlags.NONE);
@ -542,13 +548,16 @@ const VPNRequestHandler = new Lang.Class({
}
}
} catch(e) {
logError(e, 'error while reading VPN plugin output keyfile');
// No output is a valid case it means "both secrets are stored"
if (data.length > 0) {
logError(e, 'error while reading VPN plugin output keyfile');
this._agent.respond(this._requestId, Shell.NetworkAgentResponse.INTERNAL_ERROR);
return;
this._agent.respond(this._requestId, Shell.NetworkAgentResponse.INTERNAL_ERROR);
return;
}
}
if (contentOverride.secrets.length) {
if (contentOverride && contentOverride.secrets.length) {
// Only show the dialog if we actually have something to ask
this._shellDialog = new NetworkSecretDialog(this._agent, this._requestId, this._connection, 'vpn', [], contentOverride);
this._shellDialog.open(global.get_current_time());

View File

@ -54,7 +54,9 @@ const AuthenticationDialog = new Lang.Class({
text: _("Authentication Required") });
messageBox.add(this._subjectLabel,
{ y_fill: false,
{ x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
this._descriptionLabel = new St.Label({ style_class: 'prompt-dialog-description',
@ -63,7 +65,9 @@ const AuthenticationDialog = new Lang.Class({
this._descriptionLabel.clutter_text.line_wrap = true;
messageBox.add(this._descriptionLabel,
{ y_fill: true,
{ x_fill: false,
y_fill: true,
x_align: St.Align.START,
y_align: St.Align.START });
if (userNames.length > 1) {
@ -95,7 +99,8 @@ const AuthenticationDialog = new Lang.Class({
if (userIsRoot) {
let userLabel = new St.Label(({ style_class: 'polkit-dialog-user-root-label',
text: userRealName }));
messageBox.add(userLabel);
messageBox.add(userLabel, { x_fill: false,
x_align: St.Align.START });
} else {
let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout',
vertical: false });
@ -137,7 +142,7 @@ const AuthenticationDialog = new Lang.Class({
this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label' });
this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._errorMessageLabel.clutter_text.line_wrap = true;
messageBox.add(this._errorMessageLabel);
messageBox.add(this._errorMessageLabel, { x_fill: false, x_align: St.Align.START });
this._errorMessageLabel.hide();
this._infoMessageLabel = new St.Label({ style_class: 'prompt-dialog-info-label' });

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 GLib = imports.gi.GLib;
const Lang = imports.lang;
@ -13,7 +14,6 @@ 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;
@ -29,6 +29,8 @@ const SCROLLBACK_HISTORY_LINES = 10;
// See Notification._onEntryChanged
const COMPOSING_STOP_TIMEOUT = 5;
const CLOCK_FORMAT_KEY = 'clock-format';
const NotificationDirection = {
SENT: 'chat-sent',
RECEIVED: 'chat-received'
@ -100,15 +102,6 @@ const TelepathyClient = new Lang.Class({
this._tpClient.set_handle_channels_func(
Lang.bind(this, this._handleChannels));
// Watch subscription requests and connection errors
this._subscriptionSource = null;
this._accountSource = null;
// Workaround for gjs not supporting GPtrArray in signals.
// See BGO bug #653941 for context.
this._tpClient.set_contact_list_changed_func(
Lang.bind(this, this._contactListChanged));
// Allow other clients (such as Empathy) to pre-empt our channels if
// needed
this._tpClient.set_delegated_channels_callback(
@ -122,17 +115,12 @@ const TelepathyClient = new Lang.Class({
throw new Error('Couldn\'t register Telepathy client. Error: \n' + e);
}
this._accountManagerValidityChangedId = this._accountManager.connect('account-validity-changed',
Lang.bind(this, this._accountValidityChanged));
if (!this._accountManager.is_prepared(Tp.AccountManager.get_feature_quark_core()))
this._accountManager.prepare_async(null, Lang.bind(this, this._accountManagerPrepared));
},
disable: function() {
this._tpClient.unregister();
this._accountManager.disconnect(this._accountManagerValidityChangedId);
this._accountManagerValidityChangedId = 0;
},
_observeChannels: function(observer, account, conn, channels,
@ -217,33 +205,6 @@ const TelepathyClient = new Lang.Class({
}
},
_displayRoomInvitation: function(conn, channel, dispatchOp, context) {
// We can only approve the rooms if we have been invited to it
let selfContact = channel.group_get_self_contact();
if (selfContact == null) {
context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
message: 'Not invited to the room' }));
return;
}
let [invited, inviter, reason, msg] = channel.group_get_local_pending_contact_info(selfContact);
if (!invited) {
context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
message: 'Not invited to the room' }));
return;
}
// FIXME: We don't have a 'chat room' icon (bgo #653737) use
// system-users for now as Empathy does.
let source = new ApproverSource(dispatchOp, _("Invitation"),
Gio.icon_new_for_string('system-users'));
Main.messageTray.add(source);
let notif = new RoomInviteNotification(source, dispatchOp, channel, inviter);
source.notify(notif);
context.accept();
},
_approveChannels: function(approver, account, conn, channels,
dispatchOp, context) {
let channel = channels[0];
@ -257,10 +218,6 @@ const TelepathyClient = new Lang.Class({
if (chanType == Tp.IFACE_CHANNEL_TYPE_TEXT)
this._approveTextChannel(account, conn, channel, dispatchOp, context);
else if (chanType == Tp.IFACE_CHANNEL_TYPE_CALL)
this._approveCall(account, conn, channel, dispatchOp, context);
else if (chanType == Tp.IFACE_CHANNEL_TYPE_FILE_TRANSFER)
this._approveFileTransfer(account, conn, channel, dispatchOp, context);
else
context.fail(new Tp.Error({ code: Tp.Error.INVALID_ARGUMENT,
message: 'Unsupported channel type' }));
@ -281,45 +238,9 @@ const TelepathyClient = new Lang.Class({
}}));
context.accept();
} else {
this._displayRoomInvitation(conn, channel, dispatchOp, context);
}
},
_approveCall: function(account, conn, channel, dispatchOp, context) {
let isVideo = false;
let props = channel.borrow_immutable_properties();
if (props[Tp.PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO])
isVideo = true;
// We got the TpContact
let source = new ApproverSource(dispatchOp, _("Call"), isVideo ?
Gio.icon_new_for_string('camera-web') :
Gio.icon_new_for_string('audio-input-microphone'));
Main.messageTray.add(source);
let notif = new AudioVideoNotification(source, dispatchOp, channel,
channel.get_target_contact(), isVideo);
source.notify(notif);
context.accept();
},
_approveFileTransfer: function(account, conn, channel, dispatchOp, context) {
// Use the icon of the file being transferred
let gicon = Gio.content_type_get_icon(channel.get_mime_type());
// We got the TpContact
let source = new ApproverSource(dispatchOp, _("File Transfer"), gicon);
Main.messageTray.add(source);
let notif = new FileTransferNotification(source, dispatchOp, channel,
channel.get_target_contact());
source.notify(notif);
context.accept();
},
_delegatedChannelsCb: function(client, channels) {
// Nothing to do as we don't make a distinction between observed and
// handled channels.
@ -327,105 +248,7 @@ const TelepathyClient = new Lang.Class({
_accountManagerPrepared: function(am, result) {
am.prepare_finish(result);
let accounts = am.get_valid_accounts();
for (let i = 0; i < accounts.length; i++) {
this._accountValidityChanged(am, accounts[i], true);
}
},
_accountValidityChanged: function(am, account, valid) {
if (!valid)
return;
// It would be better to connect to "status-changed" but we cannot.
// See discussion in https://bugzilla.gnome.org/show_bug.cgi?id=654159
account.connect("notify::connection-status",
Lang.bind(this, this._accountConnectionStatusNotifyCb));
account.connect('notify::connection',
Lang.bind(this, this._connectionChanged));
this._connectionChanged(account);
},
_connectionChanged: function(account) {
let conn = account.get_connection();
if (conn == null)
return;
this._tpClient.grab_contact_list_changed(conn);
if (conn.get_contact_list_state() == Tp.ContactListState.SUCCESS) {
this._contactListChanged(conn, conn.dup_contact_list(), []);
}
},
_contactListChanged: function(conn, added, removed) {
for (let i = 0; i < added.length; i++) {
let contact = added[i];
contact.connect('subscription-states-changed',
Lang.bind(this, this._subscriptionStateChanged));
this._subscriptionStateChanged(contact);
}
},
_subscriptionStateChanged: function(contact) {
if (contact.get_publish_state() != Tp.SubscriptionState.ASK)
return;
/* Implicitly accept publish requests if contact is already subscribed */
if (contact.get_subscribe_state() == Tp.SubscriptionState.YES ||
contact.get_subscribe_state() == Tp.SubscriptionState.ASK) {
contact.authorize_publication_async(function(src, result) {
src.authorize_publication_finish(result)});
return;
}
/* Display notification to ask user to accept/reject request */
let source = this._ensureAppSource();
let notif = new SubscriptionRequestNotification(source, contact);
source.notify(notif);
},
_accountConnectionStatusNotifyCb: function(account) {
let connectionError = account.connection_error;
if (account.connection_status != Tp.ConnectionStatus.DISCONNECTED ||
connectionError == Tp.error_get_dbus_name(Tp.Error.CANCELLED)) {
return;
}
let notif = this._accountNotifications[account.get_object_path()];
if (notif)
return;
/* Display notification that account failed to connect */
let source = this._ensureAppSource();
notif = new AccountNotification(source, account, connectionError);
this._accountNotifications[account.get_object_path()] = notif;
notif.connect('destroy', Lang.bind(this, function() {
delete this._accountNotifications[account.get_object_path()];
}));
source.notify(notif);
},
_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._appSource;
}
});
const ChatSource = new Lang.Class({
@ -447,6 +270,7 @@ const ChatSource = new Lang.Class({
this._closedId = this._channel.connect('invalidated', Lang.bind(this, this._channelClosed));
this._notification = new ChatNotification(this);
this._notification.connect('clicked', Lang.bind(this, this.open));
this._notification.setUrgency(MessageTray.Urgency.HIGH);
this._notifyTimeoutId = 0;
@ -488,7 +312,7 @@ const ChatSource = new Lang.Class({
},
_createPolicy: function() {
return new NotificationDaemon.NotificationApplicationPolicy('empathy');
return new MessageTray.NotificationApplicationPolicy('empathy');
},
_updateAlias: function() {
@ -542,23 +366,22 @@ const ChatSource = new Lang.Class({
_updateAvatarIcon: function() {
this.iconUpdated();
this._notification.update(this._notification.title, null, { customContent: true });
this._notification.update(this._notification.title);
},
open: function(notification) {
if (this._client.is_handling_channel(this._channel)) {
// We are handling the channel, try to pass it to Empathy
this._client.delegate_channels_async([this._channel],
global.get_current_time(),
'org.freedesktop.Telepathy.Client.Empathy.Chat', null);
}
else {
// We are not the handler, just ask to present the channel
let dbus = Tp.DBusDaemon.dup();
let cd = Tp.ChannelDispatcher.new(dbus);
open: function() {
if (this._client.is_handling_channel(this._channel)) {
// We are handling the channel, try to pass it to Empathy
this._client.delegate_channels_async([this._channel],
global.get_current_time(),
'org.freedesktop.Telepathy.Client.Empathy.Chat', null);
} else {
// We are not the handler, just ask to present the channel
let dbus = Tp.DBusDaemon.dup();
let cd = Tp.ChannelDispatcher.new(dbus);
cd.present_channel_async(this._channel, global.get_current_time(), null);
}
cd.present_channel_async(this._channel, global.get_current_time(), null);
}
},
_getLogMessages: function() {
@ -622,7 +445,11 @@ const ChatSource = new Lang.Class({
this.notify();
},
_channelClosed: function() {
destroy: function(reason) {
if (this._destroyed)
return;
this._destroyed = true;
this._channel.disconnect(this._closedId);
this._channel.disconnect(this._receivedId);
this._channel.disconnect(this._pendingId);
@ -632,7 +459,14 @@ const ChatSource = new Lang.Class({
this._contact.disconnect(this._notifyAvatarId);
this._contact.disconnect(this._presenceChangedId);
this.destroy();
if (this._timestampTimeoutId)
Mainloop.source_remove(this._timestampTimeoutId);
this.parent(reason);
},
_channelClosed: function() {
this.destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED);
},
/* All messages are new messages for Telepathy sources */
@ -668,6 +502,7 @@ const ChatSource = new Lang.Class({
Mainloop.source_remove(this._notifyTimeoutId);
this._notifyTimeoutId = Mainloop.timeout_add(500,
Lang.bind(this, this._notifyTimeout));
GLib.Source.set_name_by_id(this._notifyTimeoutId, '[gnome-shell] this._notifyTimeout');
},
_notifyTimeout: function() {
@ -676,7 +511,7 @@ const ChatSource = new Lang.Class({
this._notifyTimeoutId = 0;
return false;
return GLib.SOURCE_REMOVE;
},
// This is called for both messages we send from
@ -723,7 +558,7 @@ const ChatSource = new Lang.Class({
title = GLib.markup_escape_text(this.title, -1);
this._notification.update(this._notification.title, null, { customContent: true, secondaryGIcon: this.getSecondaryIcon() });
this._notification.update(this._notification.title, null, { secondaryGIcon: this.getSecondaryIcon() });
if (message)
msg += ' <i>(' + GLib.markup_escape_text(message, -1) + ')</i>';
@ -750,8 +585,7 @@ const ChatNotification = new Lang.Class({
Extends: MessageTray.Notification,
_init: function(source) {
this.parent(source, source.title, null, { customContent: true, secondaryGIcon: source.getSecondaryIcon() });
this.setResident(true);
this.parent(source, source.title, null, { secondaryGIcon: source.getSecondaryIcon() });
this._responseEntry = new St.Entry({ style_class: 'chat-response',
can_focus: true });
@ -767,16 +601,17 @@ const ChatNotification = new Lang.Class({
this.emit('unfocused');
}));
this._createScrollArea();
this._lastGroup = null;
this._lastGroupActor = null;
this._bodyBox = new St.BoxLayout({ style_class: 'chat-notification-body-box' });
this._bodyBin.child = this._bodyBox;
// 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) {
this._oldMaxScrollValue = this._bodyScrollArea.vscroll.adjustment.value;
this._bodyScrollArea.add_style_class_name('chat-notification-scrollview');
this._bodyScrollArea.vscroll.adjustment.connect('changed', Lang.bind(this, function(adjustment) {
if (adjustment.value == this._oldMaxScrollValue)
this.scrollTo(St.Side.BOTTOM);
this._oldMaxScrollValue = Math.max(adjustment.lower, adjustment.upper - adjustment.page_size);
@ -813,8 +648,7 @@ const ChatNotification = new Lang.Class({
}
if (message.direction == NotificationDirection.RECEIVED) {
this.update(this.source.title, messageBody, { customContent: true,
bannerMarkup: true });
this.update(this.source.title, messageBody, { bannerMarkup: true });
}
let group = (message.direction == NotificationDirection.RECEIVED ?
@ -851,7 +685,7 @@ const ChatNotification = new Lang.Class({
expired[i].actor.destroy();
}
let groups = this._contentArea.get_children();
let groups = this._bodyBox.get_children();
for (let i = 0; i < groups.length; i++) {
let group = groups[i];
if (group.get_n_children() == 0)
@ -883,9 +717,9 @@ const ChatNotification = new Lang.Class({
if (this._timestampTimeoutId)
Mainloop.source_remove(this._timestampTimeoutId);
let highlighter = new MessageTray.URLHighlighter(props.body,
true, // line wrap?
true); // allow markup?
let highlighter = new MessageTray.URLHighlighter();
highlighter.actor.clutter_text.line_wrap = true;
highlighter.setMarkup(props.body, true);
let body = highlighter.actor;
@ -895,30 +729,34 @@ const ChatNotification = new Lang.Class({
let group = props.group;
if (group != this._lastGroup) {
let style = 'chat-group-' + group;
this._lastGroup = group;
this._lastGroupActor = new St.BoxLayout({ style_class: style,
vertical: true });
this.addActor(this._lastGroupActor);
let emptyLine = new St.Label({ style_class: 'chat-empty-line' });
this._bodyBox.add_child(emptyLine);
}
this._lastGroupActor.add(body, props.childProps);
this._lastMessageBox = new St.BoxLayout({ vertical: false });
this.updated();
let revealer = new MessageTray.Revealer(body);
this._lastMessageBox.add(revealer, props.childProps);
revealer.show(true);
this._bodyBox.add_child(this._lastMessageBox);
let timestamp = props.timestamp;
this._history.unshift({ actor: body, time: timestamp,
realMessage: group != 'meta' });
if (!props.noTimestamp) {
if (timestamp < currentTime - SCROLLBACK_IMMEDIATE_TIME)
if (timestamp < currentTime - SCROLLBACK_IMMEDIATE_TIME) {
this.appendTimestamp();
else
} else {
// Schedule a new timestamp in SCROLLBACK_IMMEDIATE_TIME
// from the timestamp of the message.
this._timestampTimeoutId = Mainloop.timeout_add_seconds(
SCROLLBACK_IMMEDIATE_TIME - (currentTime - timestamp),
Lang.bind(this, this.appendTimestamp));
GLib.Source.set_name_by_id(this._timestampTimeoutId, '[gnome-shell] this.appendTimestamp');
}
}
this._filterMessages();
@ -931,50 +769,98 @@ const ChatNotification = new Lang.Class({
let format;
// Show only the hour if date is on today
if(daysAgo < 1){
format = "<b>%H:%M</b>";
}
// Show the word "Yesterday" and time if date is on yesterday
else if(daysAgo <2){
/* Translators: this is the word "Yesterday" followed by a time string. i.e. "Yesterday, 14:30"*/
// xgettext:no-c-format
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 the week day name followed by a time string. i.e. "Monday, 14:30*/
// xgettext:no-c-format
format = _("<b>%A</b>, <b>%H:%M</b>");
let desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
let clockFormat = desktopSettings.get_string(CLOCK_FORMAT_KEY);
let hasAmPm = date.toLocaleFormat('%p') != '';
} else if (date.getYear() == now.getYear()) {
/* 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>%B</b> <b>%d</b>, <b>%H:%M</b>");
if (clockFormat == '24h' || !hasAmPm) {
// Show only the time if date is on today
if(daysAgo < 1){
/* Translators: Time in 24h format */
format = _("%H\u2236%M");
}
// Show the word "Yesterday" and time if date is on yesterday
else if(daysAgo <2){
/* Translators: this is the word "Yesterday" followed by a
time string in 24h format. i.e. "Yesterday, 14:30" */
// xgettext:no-c-format
format = _("Yesterday, %H\u2236%M");
}
// Show a week day and time if date is in the last week
else if (daysAgo < 7) {
/* Translators: this is the week day name followed by a time
string in 24h format. i.e. "Monday, 14:30" */
// xgettext:no-c-format
format = _("%A, %H\u2236%M");
} else if (date.getYear() == now.getYear()) {
/* Translators: this is the month name and day number
followed by a time string in 24h format.
i.e. "May 25, 14:30" */
// xgettext:no-c-format
format = _("%B %d, %H\u2236%M");
} else {
/* Translators: this is the month name, day number, year
number followed by a time string in 24h format.
i.e. "May 25 2012, 14:30" */
// xgettext:no-c-format
format = _("%B %d %Y, %H\u2236%M");
}
} else {
/* 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>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> ");
}
// Show only the time if date is on today
if(daysAgo < 1){
/* Translators: Time in 24h format */
format = _("%l\u2236%M %p");
}
// Show the word "Yesterday" and time if date is on yesterday
else if(daysAgo <2){
/* Translators: this is the word "Yesterday" followed by a
time string in 12h format. i.e. "Yesterday, 2:30 pm" */
// xgettext:no-c-format
format = _("Yesterday, %l\u2236%M %p");
}
// Show a week day and time if date is in the last week
else if (daysAgo < 7) {
/* Translators: this is the week day name followed by a time
string in 12h format. i.e. "Monday, 2:30 pm" */
// xgettext:no-c-format
format = _("%A, %l\u2236%M %p");
} else if (date.getYear() == now.getYear()) {
/* Translators: this is the month name and day number
followed by a time string in 12h format.
i.e. "May 25, 2:30 pm" */
// xgettext:no-c-format
format = _("%B %d, %l\u2236%M %p");
} else {
/* Translators: this is the month name, day number, year
number followed by a time string in 12h format.
i.e. "May 25 2012, 2:30 pm"*/
// xgettext:no-c-format
format = _("%B %d %Y, %l\u2236%M %p");
}
}
return date.toLocaleFormat(format);
},
appendTimestamp: function() {
this._timestampTimeoutId = 0;
let lastMessageTime = this._history[0].time;
let lastMessageDate = new Date(lastMessageTime * 1000);
let timeLabel = this._append({ body: this._formatTimestamp(lastMessageDate),
group: 'meta',
styles: ['chat-meta-message'],
childProps: { expand: true, x_fill: false,
x_align: St.Align.END },
noTimestamp: true,
timestamp: lastMessageTime });
let timeLabel = new St.Label({ text: this._formatTimestamp(lastMessageDate),
style_class: 'chat-meta-message',
x_expand: true,
y_expand: true,
x_align: Clutter.ActorAlign.END,
y_align: Clutter.ActorAlign.END });
this._lastMessageBox.add_actor(timeLabel);
this._filterMessages();
return false;
return GLib.SOURCE_REMOVE;
},
appendAliasChange: function(oldAlias, newAlias) {
@ -989,7 +875,7 @@ const ChatNotification = new Lang.Class({
group: 'meta',
styles: ['chat-meta-message'] });
this.update(newAlias, null, { customContent: true });
this.update(newAlias);
this._filterMessages();
},
@ -1012,7 +898,7 @@ const ChatNotification = new Lang.Class({
this.source.setChatState(Tp.ChannelChatState.PAUSED);
return false;
return GLib.SOURCE_REMOVE;
},
_onEntryChanged: function() {
@ -1035,391 +921,11 @@ const ChatNotification = new Lang.Class({
this._composingTimeoutId = Mainloop.timeout_add_seconds(
COMPOSING_STOP_TIMEOUT,
Lang.bind(this, this._composingStopTimeout));
GLib.Source.set_name_by_id(this._composingTimeoutId, '[gnome-shell] this._composingStopTimeout');
} else {
this.source.setChatState(Tp.ChannelChatState.ACTIVE);
}
}
});
const ApproverSource = new Lang.Class({
Name: 'ApproverSource',
Extends: MessageTray.Source,
_init: function(dispatchOp, text, gicon) {
this._gicon = gicon;
this.parent(text);
this._dispatchOp = dispatchOp;
// Destroy the source if the channel dispatch operation is invalidated
// as we can't approve any more.
this._invalidId = dispatchOp.connect('invalidated',
Lang.bind(this, function(domain, code, msg) {
this.destroy();
}));
},
_createPolicy: function() {
return new NotificationDaemon.NotificationApplicationPolicy('empathy');
},
destroy: function() {
if (this._invalidId != 0) {
this._dispatchOp.disconnect(this._invalidId);
this._invalidId = 0;
}
this.parent();
},
getIcon: function() {
return this._gicon;
}
});
const RoomInviteNotification = new Lang.Class({
Name: 'RoomInviteNotification',
Extends: MessageTray.Notification,
_init: function(source, dispatchOp, channel, inviter) {
this.parent(source,
/* translators: argument is a room name like
* room@jabber.org for example. */
_("Invitation to %s").format(channel.get_identifier()),
null,
{ customContent: true });
this.setResident(true);
/* translators: first argument is the name of a contact and the second
* one the name of a room. "Alice is inviting you to join room@jabber.org
* for example. */
this.addBody(_("%s is inviting you to join %s").format(inviter.get_alias(), channel.get_identifier()));
this.addButton('decline', _("Decline"));
this.addButton('accept', _("Accept"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'decline':
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE,
'', function(src, result) {
src.leave_channels_finish(result)});
break;
case 'accept':
dispatchOp.handle_with_time_async('', global.get_current_time(),
function(src, result) {
src.handle_with_time_finish(result)});
break;
}
this.destroy();
}));
}
});
// Audio Video
const AudioVideoNotification = new Lang.Class({
Name: 'AudioVideoNotification',
Extends: MessageTray.Notification,
_init: function(source, dispatchOp, channel, contact, isVideo) {
let title = '';
if (isVideo)
/* translators: argument is a contact name like Alice for example. */
title = _("Video call from %s").format(contact.get_alias());
else
/* translators: argument is a contact name like Alice for example. */
title = _("Call from %s").format(contact.get_alias());
this.parent(source, title, null, { customContent: true });
this.setResident(true);
this.setUrgency(MessageTray.Urgency.CRITICAL);
this.addButton('reject', _("Decline"));
/* translators: this is a button label (verb), not a noun */
this.addButton('answer', _("Answer"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'reject':
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE,
'', function(src, result) {
src.leave_channels_finish(result)});
break;
case 'answer':
dispatchOp.handle_with_time_async('', global.get_current_time(),
function(src, result) {
src.handle_with_time_finish(result)});
break;
}
this.destroy();
}));
}
});
// File Transfer
const FileTransferNotification = new Lang.Class({
Name: 'FileTransferNotification',
Extends: MessageTray.Notification,
_init: function(source, dispatchOp, channel, contact) {
this.parent(source,
/* To translators: The first parameter is
* the contact's alias and the second one is the
* file name. The string will be something
* like: "Alice is sending you test.ogg"
*/
_("%s is sending you %s").format(contact.get_alias(),
channel.get_filename()),
null,
{ customContent: true });
this.setResident(true);
this.addButton('decline', _("Decline"));
this.addButton('accept', _("Accept"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'decline':
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE,
'', function(src, result) {
src.leave_channels_finish(result)});
break;
case 'accept':
dispatchOp.handle_with_time_async('', global.get_current_time(),
function(src, result) {
src.handle_with_time_finish(result)});
break;
}
this.destroy();
}));
}
});
// Subscription request
const SubscriptionRequestNotification = new Lang.Class({
Name: 'SubscriptionRequestNotification',
Extends: MessageTray.Notification,
_init: function(source, contact) {
this.parent(source,
/* To translators: The parameter is the contact's alias */
_("%s would like permission to see when you are online").format(contact.get_alias()),
null, { customContent: true });
this._contact = contact;
this._connection = contact.get_connection();
let layout = new St.BoxLayout({ vertical: false });
// Display avatar
let iconBox = new St.Bin({ style_class: 'avatar-box' });
iconBox._size = 48;
let textureCache = St.TextureCache.get_default();
let file = contact.get_avatar_file();
if (file) {
let uri = file.get_uri();
iconBox.child = textureCache.load_uri_async(uri, iconBox._size, iconBox._size);
}
else {
iconBox.child = new St.Icon({ icon_name: 'avatar-default',
icon_size: iconBox._size });
}
layout.add(iconBox);
// subscription request message
let label = new St.Label({ style_class: 'subscription-message',
text: contact.get_publish_request() });
layout.add(label);
this.addActor(layout);
this.addButton('decline', _("Decline"));
this.addButton('accept', _("Accept"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'decline':
contact.remove_async(function(src, result) {
src.remove_finish(result)});
break;
case 'accept':
// Authorize the contact and request to see his status as well
contact.authorize_publication_async(function(src, result) {
src.authorize_publication_finish(result)});
contact.request_subscription_async('', function(src, result) {
src.request_subscription_finish(result)});
break;
}
// rely on _subscriptionStatesChangedCb to destroy the
// notification
}));
this._changedId = contact.connect('subscription-states-changed',
Lang.bind(this, this._subscriptionStatesChangedCb));
this._invalidatedId = this._connection.connect('invalidated',
Lang.bind(this, this.destroy));
},
destroy: function() {
if (this._changedId != 0) {
this._contact.disconnect(this._changedId);
this._changedId = 0;
}
if (this._invalidatedId != 0) {
this._connection.disconnect(this._invalidatedId);
this._invalidatedId = 0;
}
this.parent();
},
_subscriptionStatesChangedCb: function(contact, subscribe, publish, msg) {
// Destroy the notification if the subscription request has been
// answered
if (publish != Tp.SubscriptionState.ASK)
this.destroy();
}
});
// Messages from empathy/libempathy/empathy-utils.c
// create_errors_to_message_hash()
/* Translator note: these should be the same messages that are
* used in Empathy, so just copy and paste from there. */
let _connectionErrorMessages = {};
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.NETWORK_ERROR)]
= _("Network error");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.AUTHENTICATION_FAILED)]
= _("Authentication failed");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.ENCRYPTION_ERROR)]
= _("Encryption error");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_NOT_PROVIDED)]
= _("Certificate not provided");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_UNTRUSTED)]
= _("Certificate untrusted");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_EXPIRED)]
= _("Certificate expired");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_NOT_ACTIVATED)]
= _("Certificate not activated");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_HOSTNAME_MISMATCH)]
= _("Certificate hostname mismatch");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_FINGERPRINT_MISMATCH)]
= _("Certificate fingerprint mismatch");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_SELF_SIGNED)]
= _("Certificate self-signed");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CANCELLED)]
= _("Status is set to offline");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.ENCRYPTION_NOT_AVAILABLE)]
= _("Encryption is not available");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_INVALID)]
= _("Certificate is invalid");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CONNECTION_REFUSED)]
= _("Connection has been refused");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CONNECTION_FAILED)]
= _("Connection can't be established");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CONNECTION_LOST)]
= _("Connection has been lost");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.ALREADY_CONNECTED)]
= _("This account is already connected to the server");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CONNECTION_REPLACED)]
= _("Connection has been replaced by a new connection using the same resource");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.REGISTRATION_EXISTS)]
= _("The account already exists on the server");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.SERVICE_BUSY)]
= _("Server is currently too busy to handle the connection");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_REVOKED)]
= _("Certificate has been revoked");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_INSECURE)]
= _("Certificate uses an insecure cipher algorithm or is cryptographically weak");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_LIMIT_EXCEEDED)]
= _("The length of the server certificate, or the depth of the server certificate chain, exceed the limits imposed by the cryptography library");
_connectionErrorMessages['org.freedesktop.DBus.Error.NoReply']
= _("Internal error");
const AccountNotification = new Lang.Class({
Name: 'AccountNotification',
Extends: MessageTray.Notification,
_init: function(source, account, connectionError) {
this.parent(source,
/* translators: argument is the account name, like
* name@jabber.org for example. */
_("Unable to connect to %s").format(account.get_display_name()),
this._getMessage(connectionError));
this._account = account;
this.addButton('view', _("View account"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
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;
}
this.destroy();
}));
this._enabledId = account.connect('notify::enabled',
Lang.bind(this, function() {
if (!account.is_enabled())
this.destroy();
}));
this._invalidatedId = account.connect('invalidated',
Lang.bind(this, this.destroy));
this._connectionStatusId = account.connect('notify::connection-status',
Lang.bind(this, function() {
let status = account.connection_status;
if (status == Tp.ConnectionStatus.CONNECTED) {
this.destroy();
} else if (status == Tp.ConnectionStatus.DISCONNECTED) {
this.update(this.title, this._getMessage(account.connection_error));
}
}));
},
_getMessage: function(connectionError) {
let message;
if (connectionError in _connectionErrorMessages) {
message = _connectionErrorMessages[connectionError];
} else {
message = _("Unknown reason");
}
return message;
},
destroy: function() {
if (this._enabledId != 0) {
this._account.disconnect(this._enabledId);
this._enabledId = 0;
}
if (this._invalidatedId != 0) {
this._account.disconnect(this._invalidatedId);
this._invalidatedId = 0;
}
if (this._connectionStatusId != 0) {
this._account.disconnect(this._connectionStatusId);
this._connectionStatusId = 0;
}
this.parent();
}
});
const Component = TelepathyClient;

View File

@ -87,7 +87,7 @@ const CtrlAltTabManager = new Lang.Class({
if (Main.sessionMode.hasWindows && !Main.overview.visible) {
let screen = global.screen;
let display = screen.get_display();
let windows = display.get_tab_list(Meta.TabList.DOCKS, screen, screen.get_active_workspace ());
let windows = display.get_tab_list(Meta.TabList.DOCKS, screen.get_active_workspace ());
let windowTracker = Shell.WindowTracker.get_default();
let textureCache = St.TextureCache.get_default();
for (let i = 0; i < windows.length; i++) {
@ -165,6 +165,10 @@ const CtrlAltTabPopup = new Lang.Class({
this._select(this._previous());
else if (keysym == Clutter.Right)
this._select(this._next());
else
return Clutter.EVENT_PROPAGATE;
return Clutter.EVENT_STOP;
},
_finish : function(time) {

View File

@ -1,6 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Signals = imports.signals;
const Lang = imports.lang;
const Meta = imports.gi.Meta;
@ -380,6 +381,8 @@ const DashActor = new Lang.Class({
}
});
const baseIconSizes = [ 16, 22, 24, 32, 48, 64 ];
const Dash = new Lang.Class({
Name: 'Dash',
@ -576,8 +579,10 @@ const Dash = new Lang.Class({
Lang.bind(this, function() {
this._labelShowing = true;
item.showLabel();
return false;
this._showLabelTimeoutId = 0;
return GLib.SOURCE_REMOVE;
}));
GLib.Source.set_name_by_id(this._showLabelTimeoutId, '[gnome-shell] item.showLabel');
if (this._resetHoverTimeoutId > 0) {
Mainloop.source_remove(this._resetHoverTimeoutId);
this._resetHoverTimeoutId = 0;
@ -592,8 +597,10 @@ const Dash = new Lang.Class({
this._resetHoverTimeoutId = Mainloop.timeout_add(DASH_ITEM_HOVER_TIMEOUT,
Lang.bind(this, function() {
this._labelShowing = false;
return false;
this._resetHoverTimeoutId = 0;
return GLib.SOURCE_REMOVE;
}));
GLib.Source.set_name_by_id(this._resetHoverTimeoutId, '[gnome-shell] this._labelShowing');
}
}
},
@ -629,25 +636,24 @@ const Dash = new Lang.Class({
let minHeight, natHeight;
// Enforce the current icon size during the size request
let [currentWidth, currentHeight] = firstIcon.icon.get_size();
firstIcon.icon.set_size(this.iconSize, this.iconSize);
firstIcon.setIconSize(this.iconSize);
[minHeight, natHeight] = firstButton.get_preferred_height(-1);
firstIcon.icon.set_size(currentWidth, currentHeight);
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
let iconSizes = baseIconSizes.map(function(s) {
return s * scaleFactor;
});
// Subtract icon padding and box spacing from the available height
availHeight -= iconChildren.length * (natHeight - this.iconSize) +
availHeight -= iconChildren.length * (natHeight - this.iconSize * scaleFactor) +
(iconChildren.length - 1) * spacing;
let availSize = availHeight / iconChildren.length;
let iconSizes = [ 16, 22, 24, 32, 48, 64 ];
let newIconSize = 16;
let newIconSize = baseIconSizes[0];
for (let i = 0; i < iconSizes.length; i++) {
if (iconSizes[i] < availSize)
newIconSize = iconSizes[i];
newIconSize = baseIconSizes[i];
}
if (newIconSize == this.iconSize)

View File

@ -63,9 +63,17 @@ const DateMenuButton = new Lang.Class({
hbox.add(vbox);
// Date
this._date = new St.Label({ style_class: 'datemenu-date-label',
can_focus: true });
vbox.add(this._date);
// Having the ability to go to the current date if the user is already
// on the current date can be confusing. So don't make the button reactive
// until the selected date changes.
this._date = new St.Button({ style_class: 'datemenu-date-label',
reactive: false
});
this._date.connect('clicked',
Lang.bind(this, function() {
this._calendar.setDate(new Date(), false);
}));
vbox.add(this._date, { x_fill: false });
this._eventList = new Calendar.EventsList();
this._calendar = new Calendar.Calendar();
@ -77,6 +85,9 @@ const DateMenuButton = new Lang.Class({
// and the calender makes those dates unclickable when instantiated with
// a null event source
this._eventList.setDate(date);
// Make the button reactive only if the selected date is not the current date.
this._date.can_focus = this._date.reactive = !this._isToday(date)
}));
vbox.add(this._calendar.actor);
@ -113,22 +124,7 @@ const DateMenuButton = new Lang.Class({
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
if (isOpen) {
let now = new Date();
/* Passing true to setDate() forces events to be reloaded. We
* want this behavior, because
*
* o It will cause activation of the calendar server which is
* useful if it has crashed
*
* o It will cause the calendar server to reload events which
* is useful if dynamic updates are not supported or not
* properly working
*
* Since this only happens when the menu is opened, the cost
* isn't very big.
*/
this._calendar.setDate(now, true);
// No need to update this._eventList as ::selected-date-changed
// signal will fire
this._calendar.setDate(now);
}
}));
@ -142,6 +138,13 @@ const DateMenuButton = new Lang.Class({
this._sessionUpdated();
},
_isToday: function(date) {
let now = new Date();
return now.getYear() == date.getYear() &&
now.getMonth() == date.getMonth() &&
now.getDay() == date.getDay();
},
_appInstalledChanged: function() {
this._calendarApp = undefined;
this._updateEventsVisibility();
@ -201,7 +204,7 @@ const DateMenuButton = new Lang.Class({
*/
let dateFormat = _("%A %B %e, %Y");
let displayDate = new Date();
this._date.set_text(displayDate.toLocaleFormat(dateFormat));
this._date.set_label(displayDate.toLocaleFormat(dateFormat));
},
_getCalendarApp: function() {
@ -217,7 +220,7 @@ const DateMenuButton = new Lang.Class({
},
_getClockApp: function() {
return Shell.AppSystem.get_default().lookup_app('gnome-clocks.desktop');
return Shell.AppSystem.get_default().lookup_app('org.gnome.clocks.desktop');
},
_onOpenCalendarActivate: function() {
@ -226,7 +229,7 @@ const DateMenuButton = new Lang.Class({
let app = this._getCalendarApp();
if (app.get_id() == 'evolution.desktop')
app = Gio.DesktopAppInfo.new('evolution-calendar.desktop');
app.launch([], global.create_app_launch_context());
app.launch([], global.create_app_launch_context(0, -1));
},
_onOpenClocksActivate: function() {

View File

@ -106,10 +106,10 @@ const _Draggable = new Lang.Class({
_onButtonPress : function (actor, event) {
if (event.get_button() != 1)
return false;
return Clutter.EVENT_PROPAGATE;
if (Tweener.getTweenCount(actor))
return false;
return Clutter.EVENT_PROPAGATE;
this._buttonDown = true;
this._grabActor();
@ -118,7 +118,7 @@ const _Draggable = new Lang.Class({
this._dragStartX = stageX;
this._dragStartY = stageY;
return false;
return Clutter.EVENT_PROPAGATE;
},
_grabActor: function() {
@ -164,11 +164,11 @@ const _Draggable = new Lang.Class({
} else if (this._dragActor != null && !this._animationInProgress) {
// Drag must have been cancelled with Esc.
this._dragComplete();
return true;
return Clutter.EVENT_STOP;
} else {
// Drag has never started.
this._ungrabActor();
return false;
return Clutter.EVENT_PROPAGATE;
}
// We intercept MOTION event to figure out if the drag has started and to draw
// this._dragActor under the pointer when dragging is in progress
@ -184,11 +184,11 @@ const _Draggable = new Lang.Class({
let symbol = event.get_key_symbol();
if (symbol == Clutter.Escape) {
this._cancelDrag(event.get_time());
return true;
return Clutter.EVENT_STOP;
}
}
return false;
return Clutter.EVENT_PROPAGATE;
},
/**
@ -236,7 +236,7 @@ const _Draggable = new Lang.Class({
if (this.actor._delegate && this.actor._delegate.getDragActor) {
this._dragActor = this.actor._delegate.getDragActor();
this._dragActor.reparent(Main.uiGroup);
Main.uiGroup.add_child(this._dragActor);
this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true);
@ -285,7 +285,8 @@ const _Draggable = new Lang.Class({
this._dragOffsetX = actorStageX - this._dragStartX;
this._dragOffsetY = actorStageY - this._dragStartY;
this._dragActor.reparent(Main.uiGroup);
this._dragOrigParent.remove_actor(this._dragActor);
Main.uiGroup.add_child(this._dragActor);
this._dragActor.raise_top();
Shell.util_set_hidden_from_pick(this._dragActor, true);
}
@ -345,6 +346,7 @@ const _Draggable = new Lang.Class({
},
_updateDragHover : function () {
this._updateHoverId = 0;
let target = this._dragActor.get_stage().get_actor_at_pos(Clutter.PickMode.ALL,
this._dragX, this._dragY);
let dragEvent = {
@ -360,7 +362,7 @@ const _Draggable = new Lang.Class({
let result = motionFunc(dragEvent);
if (result != DragMotionResult.CONTINUE) {
global.screen.set_cursor(DRAG_CURSOR_MAP[result]);
return false;
return GLib.SOURCE_REMOVE;
}
}
}
@ -378,21 +380,22 @@ const _Draggable = new Lang.Class({
0);
if (result != DragMotionResult.CONTINUE) {
global.screen.set_cursor(DRAG_CURSOR_MAP[result]);
return false;
return GLib.SOURCE_REMOVE;
}
}
target = target.get_parent();
}
global.screen.set_cursor(Meta.Cursor.DND_IN_DRAG);
return false;
return GLib.SOURCE_REMOVE;
},
_queueUpdateDragHover: function() {
if (this._updateHoverId)
GLib.source_remove(this._updateHoverId);
return;
this._updateHoverId = GLib.idle_add(GLib.PRIORITY_DEFAULT,
Lang.bind(this, this._updateDragHover));
GLib.Source.set_name_by_id(this._updateHoverId, '[gnome-shell] this._updateDragHover');
},
_updateDragPosition : function (event) {
@ -555,7 +558,8 @@ const _Draggable = new Lang.Class({
_onAnimationComplete : function (dragActor, eventTime) {
if (this._dragOrigParent) {
dragActor.reparent(this._dragOrigParent);
Main.uiGroup.remove_child(this._dragActor);
this._dragOrigParent.add_actor(this._dragActor);
dragActor.set_scale(this._dragOrigScale, this._dragOrigScale);
dragActor.set_position(this._dragOrigX, this._dragOrigY);
} else {

View File

@ -13,9 +13,7 @@
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
const Lang = imports.lang;
@ -27,9 +25,11 @@ const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Pango = imports.gi.Pango;
const Polkit = imports.gi.Polkit;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const CheckBox = imports.ui.checkBox;
const GnomeSession = imports.misc.gnomeSession;
const LoginManager = imports.misc.loginManager;
const ModalDialog = imports.ui.modalDialog;
@ -38,25 +38,29 @@ const UserWidget = imports.ui.userWidget;
let _endSessionDialog = null;
const TRIGGER_OFFLINE_UPDATE = '/usr/libexec/pk-trigger-offline-update';
const _ITEM_ICON_SIZE = 48;
const _DIALOG_ICON_SIZE = 32;
const _DIALOG_ICON_SIZE = 48;
const GSM_SESSION_MANAGER_LOGOUT_FORCE = 2;
const EndSessionDialogIface = <interface name="org.gnome.SessionManager.EndSessionDialog">
<method name="Open">
<arg type="u" direction="in" />
<arg type="u" direction="in" />
<arg type="u" direction="in" />
<arg type="ao" direction="in" />
</method>
<method name="Close" />
<signal name="ConfirmedLogout" />
<signal name="ConfirmedReboot" />
<signal name="ConfirmedShutdown" />
<signal name="Canceled" />
<signal name="Closed" />
</interface>;
const EndSessionDialogIface = '<node> \
<interface name="org.gnome.SessionManager.EndSessionDialog"> \
<method name="Open"> \
<arg type="u" direction="in" /> \
<arg type="u" direction="in" /> \
<arg type="u" direction="in" /> \
<arg type="ao" direction="in" /> \
</method> \
<method name="Close" /> \
<signal name="ConfirmedLogout" /> \
<signal name="ConfirmedReboot" /> \
<signal name="ConfirmedShutdown" /> \
<signal name="Canceled" /> \
<signal name="Closed" /> \
</interface> \
</node>';
const logoutDialogContent = {
subjectWithUser: C_("title", "Log Out %s"),
@ -71,6 +75,7 @@ const logoutDialogContent = {
"You will be logged out automatically in %d seconds.",
seconds).format(seconds);
},
showBatteryWarning: false,
confirmButtons: [{ signal: 'ConfirmedLogout',
label: C_("button", "Log Out") }],
iconStyleClass: 'end-session-dialog-logout-icon',
@ -79,11 +84,14 @@ const logoutDialogContent = {
const shutdownDialogContent = {
subject: C_("title", "Power Off"),
subjectWithUpdates: C_("title", "Install Updates & Power Off"),
description: function(seconds) {
return ngettext("The system will power off automatically in %d second.",
"The system will power off automatically in %d seconds.",
seconds).format(seconds);
},
checkBoxText: C_("checkbox", "Install pending software updates"),
showBatteryWarning: true,
confirmButtons: [{ signal: 'ConfirmedReboot',
label: C_("button", "Restart") },
{ signal: 'ConfirmedShutdown',
@ -100,6 +108,7 @@ const restartDialogContent = {
"The system will restart automatically in %d seconds.",
seconds).format(seconds);
},
showBatteryWarning: false,
confirmButtons: [{ signal: 'ConfirmedReboot',
label: C_("button", "Restart") }],
iconName: 'view-refresh-symbolic',
@ -115,8 +124,11 @@ const restartInstallDialogContent = {
"The system will automatically restart and install updates in %d seconds.",
seconds).format(seconds);
},
showBatteryWarning: true,
confirmButtons: [{ signal: 'ConfirmedReboot',
label: C_("button", "Restart & Install") }],
label: C_("button", "Restart &amp; Install") }],
unusedFutureButtonForTranslation: C_("button", "Install &amp; Power Off"),
unusedFutureCheckBoxForTranslation: C_("checkbox", "Power off after updates are installed"),
iconName: 'view-refresh-symbolic',
iconStyleClass: 'end-session-dialog-shutdown-icon',
showOtherSessions: true,
@ -131,16 +143,26 @@ const DialogContent = {
const MAX_USERS_IN_SESSION_DIALOG = 5;
const LogindSessionIface = <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 LogindSessionIface = '<node> \
<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> \
</node>';
const LogindSession = Gio.DBusProxy.makeProxyWrapper(LogindSessionIface);
const UPowerIface = '<node> \
<interface name="org.freedesktop.UPower"> \
<property name="OnBattery" type="b" access="read"/> \
</interface> \
</node>';
const UPowerProxy = Gio.DBusProxy.makeProxyWrapper(UPowerIface);
function findAppFromInhibitor(inhibitor) {
let desktopFile;
try {
@ -193,6 +215,18 @@ function _setLabelText(label, text) {
}
}
function _setCheckBoxLabel(checkBox, text) {
let label = checkBox.getLabelActor();
if (text) {
label.set_text(text);
checkBox.actor.show();
} else {
label.set_text('');
checkBox.actor.hide();
}
}
function init() {
// This always returns the same singleton object
// By instantiating it initially, we register the
@ -212,6 +246,20 @@ const EndSessionDialog = new Lang.Class({
this._userManager = AccountsService.UserManager.get_default();
this._user = this._userManager.get_user(GLib.get_user_name());
this._updatesFile = Gio.File.new_for_path('/system-update');
this._preparedUpdateFile = Gio.File.new_for_path('/var/lib/PackageKit/prepared-update');
this._powerProxy = new UPowerProxy(Gio.DBus.system,
'org.freedesktop.UPower',
'/org/freedesktop/UPower',
Lang.bind(this, function(proxy, error) {
if (error) {
log(error.message);
return;
}
this._powerProxy.connect('g-properties-changed',
Lang.bind(this, this._sync));
this._sync();
}));
this._secondsLeft = 0;
this._totalSecondsToStayOpen = 0;
@ -238,14 +286,17 @@ const EndSessionDialog = new Lang.Class({
x_align: St.Align.END,
y_align: St.Align.START });
let messageLayout = new St.BoxLayout({ vertical: true });
let messageLayout = new St.BoxLayout({ vertical: true,
style_class: 'end-session-dialog-layout' });
mainContentLayout.add(messageLayout,
{ y_align: St.Align.START });
this._subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject' });
messageLayout.add(this._subjectLabel,
{ y_fill: false,
{ x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
this._descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description' });
@ -256,6 +307,16 @@ const EndSessionDialog = new Lang.Class({
{ y_fill: true,
y_align: St.Align.START });
this._checkBox = new CheckBox.CheckBox();
this._checkBox.actor.connect('clicked', Lang.bind(this, this._sync));
messageLayout.add(this._checkBox.actor);
this._batteryWarning = new St.Label({ style_class: 'end-session-dialog-warning',
text: _("Running on battery power: please plug in before installing updates.") });
this._batteryWarning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._batteryWarning.clutter_text.line_wrap = true;
messageLayout.add(this._batteryWarning);
this._scrollView = new St.ScrollView({ style_class: 'end-session-dialog-list' });
this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
this.contentLayout.add(this._scrollView,
@ -269,16 +330,24 @@ const EndSessionDialog = new Lang.Class({
this._applicationHeader = new St.Label({ style_class: 'end-session-dialog-list-header',
text: _("Some applications are busy or have unsaved work.") });
this._applicationList = new St.BoxLayout({ vertical: true });
this._applicationList = new St.BoxLayout({ style_class: 'end-session-dialog-app-list',
vertical: true });
this._inhibitorSection.add_actor(this._applicationHeader);
this._inhibitorSection.add_actor(this._applicationList);
this._sessionHeader = new St.Label({ style_class: 'end-session-dialog-list-header',
text: _("Other users are logged in.") });
this._sessionList = new St.BoxLayout({ vertical: true });
this._sessionList = new St.BoxLayout({ style_class: 'end-session-dialog-session-list',
vertical: true });
this._inhibitorSection.add_actor(this._sessionHeader);
this._inhibitorSection.add_actor(this._sessionList);
try {
this._updatesPermission = Polkit.Permission.new_sync("org.freedesktop.packagekit.trigger-offline-update", null, null);
} catch(e) {
log('No permission to trigger offline updates: %s'.format(e.toString()));
}
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(EndSessionDialogIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/SessionManager/EndSessionDialog');
},
@ -293,13 +362,22 @@ const EndSessionDialog = new Lang.Class({
if (!open)
return;
if (this._type == 2 && this._updatesFile.query_exists(null))
this._type = 3;
let dialogContent = DialogContent[this._type];
let subject = dialogContent.subject;
// Use different title when we are installing updates
if (dialogContent.subjectWithUpdates && this._checkBox.actor.checked)
subject = dialogContent.subjectWithUpdates;
if (dialogContent.showBatteryWarning) {
// Warn when running on battery power
if (this._powerProxy.OnBattery && this._checkBox.actor.checked)
this._batteryWarning.opacity = 255;
else
this._batteryWarning.opacity = 0;
}
let description;
let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
this._secondsLeft,
@ -382,15 +460,75 @@ const EndSessionDialog = new Lang.Class({
},
_confirm: function(signal) {
this._fadeOutDialog();
this._stopTimer();
this._dbusImpl.emit_signal(signal, null);
let callback = Lang.bind(this, function() {
this._fadeOutDialog();
this._stopTimer();
this._dbusImpl.emit_signal(signal, null);
});
// Offline update not available; just emit the signal
if (!this._checkBox.actor.visible) {
callback();
return;
}
// Trigger the offline update as requested
if (this._checkBox.actor.checked) {
switch (signal) {
case "ConfirmedReboot":
this._triggerOfflineUpdateReboot(callback);
break;
case "ConfirmedShutdown":
// To actually trigger the offline update, we need to
// reboot to do the upgrade. When the upgrade is complete,
// the computer will shut down automatically.
signal = "ConfirmedReboot";
this._triggerOfflineUpdateShutdown(callback);
break;
default:
callback();
break;
}
} else {
this._triggerOfflineUpdateCancel(callback);
}
},
_onOpened: function() {
this._sync();
},
_triggerOfflineUpdateReboot: function(callback) {
this._pkexecSpawn([TRIGGER_OFFLINE_UPDATE, 'reboot'], callback);
},
_triggerOfflineUpdateShutdown: function(callback) {
this._pkexecSpawn([TRIGGER_OFFLINE_UPDATE, 'power-off'], callback);
},
_triggerOfflineUpdateCancel: function(callback) {
this._pkexecSpawn([TRIGGER_OFFLINE_UPDATE, '--cancel'], callback);
},
_pkexecSpawn: function(argv, callback) {
let ret, pid;
try {
[ret, pid] = GLib.spawn_async(null, ['pkexec'].concat(argv), null,
GLib.SpawnFlags.DO_NOT_REAP_CHILD | GLib.SpawnFlags.SEARCH_PATH,
null);
} catch (e) {
log('Error spawning "pkexec %s": %s'.format(argv.join(' '), e.toString()));
callback();
return;
}
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) {
GLib.spawn_close_pid(pid);
callback();
});
},
_startTimer: function() {
let startTime = GLib.get_monotonic_time();
this._secondsLeft = this._totalSecondsToStayOpen;
@ -403,15 +541,17 @@ const EndSessionDialog = new Lang.Class({
this._secondsLeft = this._totalSecondsToStayOpen - secondsElapsed;
if (this._secondsLeft > 0) {
this._sync();
return true;
return GLib.SOURCE_CONTINUE;
}
let dialogContent = DialogContent[this._type];
let button = dialogContent.confirmButtons[dialogContent.confirmButtons.length - 1];
this._confirm(button.signal);
this._timerId = 0;
return false;
return GLib.SOURCE_REMOVE;
}));
GLib.Source.set_name_by_id(this._timerId, '[gnome-shell] this._confirm');
},
_stopTimer: function() {
@ -537,6 +677,9 @@ const EndSessionDialog = new Lang.Class({
this._totalSecondsToStayOpen = totalSecondsToStayOpen;
this._type = type;
if (this._type == 2 && this._updatesFile.query_exists(null))
this._type = 3;
this._applications = [];
this._applicationList.destroy_all_children();
@ -549,6 +692,8 @@ const EndSessionDialog = new Lang.Class({
return;
}
let dialogContent = DialogContent[this._type];
for (let i = 0; i < inhibitorObjectPaths.length; i++) {
let inhibitor = new GnomeSession.Inhibitor(inhibitorObjectPaths[i], Lang.bind(this, function(proxy, error) {
this._onInhibitorLoaded(proxy);
@ -557,9 +702,23 @@ const EndSessionDialog = new Lang.Class({
this._applications.push(inhibitor);
}
if (DialogContent[type].showOtherSessions)
if (dialogContent.showOtherSessions)
this._loadSessions();
let preparedUpdate = this._preparedUpdateFile.query_exists(null);
let updateAlreadyTriggered = this._updatesFile.query_exists(null);
let updatesAllowed = this._updatesPermission && this._updatesPermission.allowed;
_setCheckBoxLabel(this._checkBox, dialogContent.checkBoxText);
this._checkBox.actor.visible = (dialogContent.checkBoxText && preparedUpdate && updatesAllowed);
this._checkBox.actor.checked = (preparedUpdate && updateAlreadyTriggered);
// We show the warning either together with the checkbox, or when
// updates have already been triggered, but the user doesn't have
// enough permissions to cancel them.
this._batteryWarning.visible = (dialogContent.showBatteryWarning &&
(this._checkBox.actor.visible || preparedUpdate && updateAlreadyTriggered && !updatesAllowed));
this._updateButtons();
if (!this.open(timestamp)) {

View File

@ -5,6 +5,8 @@ imports.gi.versions.Gio = '2.0';
imports.gi.versions.Gdk = '3.0';
imports.gi.versions.GdkPixbuf = '2.0';
imports.gi.versions.Gtk = '3.0';
imports.gi.versions.TelepathyGLib = '0.12';
imports.gi.versions.TelepathyLogger = '0.2';
const Clutter = imports.gi.Clutter;;
const Gettext = imports.gettext;
@ -45,8 +47,11 @@ function _patchLayoutClass(layoutClass, styleProps) {
layoutClass.prototype.hookup_style = function(container) {
container.connect('style-changed', Lang.bind(this, function() {
let node = container.get_theme_node();
for (let prop in styleProps)
this[prop] = node.get_length(styleProps[prop]);
for (let prop in styleProps) {
let [found, length] = node.lookup_length(styleProps[prop], false);
if (found)
this[prop] = length;
}
}));
};
layoutClass.prototype.child_set = function(actor, props) {

View File

@ -201,7 +201,7 @@ const InstallExtensionDialog = new Lang.Class({
default: true
}]);
let message = _("Download and install '%s' from extensions.gnome.org?").format(info.name);
let message = _("Download and install %s from extensions.gnome.org?").format(info.name);
let box = new St.BoxLayout();
this.contentLayout.add(box);

View File

@ -38,6 +38,7 @@ const connect = Lang.bind(_signals, _signals.connect);
const disconnect = Lang.bind(_signals, _signals.disconnect);
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
const EXTENSION_DISABLE_VERSION_CHECK_KEY = 'disable-extension-version-validation';
var initted = false;
var enabled;
@ -76,7 +77,11 @@ function disableExtension(uuid) {
theme.unload_stylesheet(extension.stylesheet.get_path());
}
extension.stateObj.disable();
try {
extension.stateObj.disable();
} catch(e) {
logExtensionError(uuid, e);
}
for (let i = 0; i < order.length; i++) {
let uuid = order[i];
@ -89,8 +94,10 @@ function disableExtension(uuid) {
extensionOrder.splice(orderIdx, 1);
extension.state = ExtensionState.DISABLED;
_signals.emit('extension-state-changed', extension);
if ( extension.state != ExtensionState.ERROR ) {
extension.state = ExtensionState.DISABLED;
_signals.emit('extension-state-changed', extension);
}
}
function enableExtension(uuid) {
@ -117,10 +124,15 @@ function enableExtension(uuid) {
}
}
extension.stateObj.enable();
extension.state = ExtensionState.ENABLED;
_signals.emit('extension-state-changed', extension);
try {
extension.stateObj.enable();
extension.state = ExtensionState.ENABLED;
_signals.emit('extension-state-changed', extension);
return;
} catch(e) {
logExtensionError(uuid, e);
return;
}
}
function logExtensionError(uuid, error) {
@ -145,12 +157,15 @@ function loadExtension(extension) {
// Default to error, we set success as the last step
extension.state = ExtensionState.ERROR;
if (ExtensionUtils.isOutOfDate(extension)) {
let checkVersion = !global.settings.get_boolean(EXTENSION_DISABLE_VERSION_CHECK_KEY);
if (checkVersion && ExtensionUtils.isOutOfDate(extension)) {
extension.state = ExtensionState.OUT_OF_DATE;
} else {
let enabled = enabledExtensions.indexOf(extension.uuid) != -1;
if (enabled) {
initExtension(extension.uuid);
if (!initExtension(extension.uuid))
return;
if (extension.state == ExtensionState.DISABLED)
enableExtension(extension.uuid);
} else {
@ -205,7 +220,12 @@ function initExtension(uuid) {
extensionModule = extension.imports.extension;
if (extensionModule.init) {
extensionState = extensionModule.init(extension);
try {
extensionState = extensionModule.init(extension);
} catch(e) {
logExtensionError(uuid, e);
return false;
}
}
if (!extensionState)
@ -214,6 +234,7 @@ function initExtension(uuid) {
extension.state = ExtensionState.DISABLED;
_signals.emit('extension-loaded', uuid);
return true;
}
function getEnabledExtensions() {
@ -235,11 +256,7 @@ function onEnabledExtensionsChanged() {
newEnabledExtensions.filter(function(uuid) {
return enabledExtensions.indexOf(uuid) == -1;
}).forEach(function(uuid) {
try {
enableExtension(uuid);
} catch(e) {
logExtensionError(uuid, e);
}
enableExtension(uuid);
});
// Find and disable all the newly disabled extensions: UUIDs found in the
@ -247,27 +264,30 @@ function onEnabledExtensionsChanged() {
enabledExtensions.filter(function(item) {
return newEnabledExtensions.indexOf(item) == -1;
}).forEach(function(uuid) {
try {
disableExtension(uuid);
} catch(e) {
logExtensionError(uuid, e);
}
disableExtension(uuid);
});
enabledExtensions = newEnabledExtensions;
}
function _onVersionValidationChanged() {
if (Main.sessionMode.allowExtensions) {
enabledExtensions.forEach(function(uuid) {
if (ExtensionUtils.extensions[uuid])
reloadExtension(ExtensionUtils.extensions[uuid]);
});
}
}
function _loadExtensions() {
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
global.settings.connect('changed::' + EXTENSION_DISABLE_VERSION_CHECK_KEY, _onVersionValidationChanged);
enabledExtensions = getEnabledExtensions();
let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', function(signals, extension) {
try {
loadExtension(extension);
} catch(e) {
logExtensionError(extension.uuid, e);
}
finder.connect('extension-found', function(finder, extension) {
loadExtension(extension);
});
finder.scanExtensions();
}

View File

@ -34,7 +34,11 @@ const FocusCaretTracker = new Lang.Class({
_init: function() {
Atspi.init();
Atspi.set_timeout(250, 250);
this._atspiListener = Atspi.EventListener.new(Lang.bind(this, this._onChanged));
this._focusListenerRegistered = false;
this._caretListenerRegistered = false;
},
_onChanged: function(event) {
@ -45,21 +49,39 @@ const FocusCaretTracker = new Lang.Class({
},
registerFocusListener: function() {
return this._atspiListener.register(STATECHANGED + ':focused') &&
this._atspiListener.register(STATECHANGED + ':selected');
if (this._focusListenerRegistered)
return;
// Ignore the return value, we get an exception if they fail
// And they should never fail
this._atspiListener.register(STATECHANGED + ':focused');
this._atspiListener.register(STATECHANGED + ':selected');
this._focusListenerRegistered = true;
},
registerCaretListener: function() {
return this._atspiListener.register(CARETMOVED);
if (this._caretListenerRegistered)
return;
this._atspiListener.register(CARETMOVED);
this._caretListenerRegistered = true;
},
deregisterFocusListener: function() {
return this._atspiListener.deregister(STATECHANGED + ':focused') &&
this._atspiListener.deregister(STATECHANGED + ':selected');
if (!this._focusListenerRegistered)
return;
this._atspiListener.deregister(STATECHANGED + ':focused');
this._atspiListener.deregister(STATECHANGED + ':selected');
this._focusListenerRegistered = false;
},
deregisterCaretListener: function() {
return this._atspiListener.deregister(CARETMOVED);
if (!this._caretListenerRegistered)
return;
this._atspiListener.deregister(CARETMOVED);
this._caretListenerRegistered = false;
}
});
Signals.addSignalMethods(FocusCaretTracker.prototype);

View File

@ -280,7 +280,7 @@ const GrabHelper = new Lang.Class({
if (type == Clutter.EventType.KEY_PRESS &&
event.get_key_symbol() == Clutter.KEY_Escape) {
this.ungrab({ isUser: true });
return true;
return Clutter.EVENT_STOP;
}
let press = type == Clutter.EventType.BUTTON_PRESS;
@ -289,14 +289,14 @@ const GrabHelper = new Lang.Class({
if (release && this._ignoreRelease) {
this._ignoreRelease = false;
return true;
return Clutter.EVENT_STOP;
}
if (this._isWithinGrabbedActor(event.get_source()))
return false;
return Clutter.EVENT_PROPAGATE;
if (Main.keyboard.shouldTakeEvent(event))
return false;
return Clutter.EVENT_PROPAGATE;
if (button) {
// If we have a press event, ignore the next event,
@ -305,9 +305,9 @@ const GrabHelper = new Lang.Class({
this._ignoreRelease = true;
let i = this._actorInGrabStack(event.get_source()) + 1;
this.ungrab({ actor: this._grabStack[i].actor, isUser: true });
return true;
return Clutter.EVENT_STOP;
}
return true;
return Clutter.EVENT_STOP;
},
});

View File

@ -32,6 +32,7 @@ const CandidateArea = new Lang.Class({
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());
return Clutter.EVENT_PROPAGATE;
}));
}
@ -114,9 +115,6 @@ const CandidatePopup = new Lang.Class({
Name: 'CandidatePopup',
_init: function() {
this._cursor = new St.Bin({ opacity: 0 });
Main.uiGroup.add_actor(this._cursor);
this._boxPointer = new BoxPointer.BoxPointer(St.Side.TOP);
this._boxPointer.actor.visible = false;
this._boxPointer.actor.style_class = 'candidate-popup-boxpointer';
@ -157,10 +155,9 @@ const CandidatePopup = new Lang.Class({
panelService.connect('set-cursor-location',
Lang.bind(this, function(ps, x, y, w, h) {
this._cursor.set_position(x, y);
this._cursor.set_size(w, h);
Main.layoutManager.setDummyCursorGeometry(x, y, w, h);
if (this._boxPointer.actor.visible)
this._boxPointer.setPosition(this._cursor, 0);
this._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0);
}));
panelService.connect('update-preedit-text',
Lang.bind(this, function(ps, text, cursorPosition, visible) {
@ -252,7 +249,7 @@ const CandidatePopup = new Lang.Class({
this._candidateArea.actor.visible);
if (isVisible) {
this._boxPointer.setPosition(this._cursor, 0);
this._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0);
this._boxPointer.show(BoxPointer.PopupAnimation.NONE);
this._boxPointer.actor.raise_top();
} else {

View File

@ -143,11 +143,6 @@ const BaseIcon = new Lang.Class({
this.icon = this.createIcon(this.iconSize);
this._iconBin.child = this.icon;
// The icon returned by createIcon() might actually be smaller than
// the requested icon size (for instance StTextureCache does this
// for fallback icons), so set the size explicitly.
this._iconBin.set_size(this.iconSize, this.iconSize);
},
_onStyleChanged: function() {
@ -219,6 +214,20 @@ const IconGrid = new Lang.Class({
this._grid.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this._grid.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this._grid.connect('allocate', Lang.bind(this, this._allocate));
this._grid.connect('actor-added', Lang.bind(this, this._childAdded));
this._grid.connect('actor-removed', Lang.bind(this, this._childRemoved));
},
_keyFocusIn: function(actor) {
this.emit('key-focus-in', actor);
},
_childAdded: function(grid, child) {
child._iconGridKeyFocusInId = child.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
},
_childRemoved: function(grid, child) {
child.disconnect(child._iconGridKeyFocusInId);
},
_getPreferredWidth: function (grid, forHeight, alloc) {
@ -413,15 +422,18 @@ const IconGrid = new Lang.Class({
},
removeAll: function() {
this._items = [];
this._grid.remove_all_children();
},
destroyAll: function() {
this._items = [];
this._grid.destroy_all_children();
},
addItem: function(item, index) {
if (!item.icon || !item.icon instanceof BaseIcon) {
log('Only items with a BaseIcon icon property can be added to IconGrid');
return;
}
if (!item.icon instanceof BaseIcon)
throw new Error('Only items with a BaseIcon icon property can be added to IconGrid');
this._items.push(item);
if (index !== undefined)
@ -430,6 +442,10 @@ const IconGrid = new Lang.Class({
this._grid.add_actor(item.actor);
},
removeItem: function(item) {
this._grid.remove_child(item.actor);
},
getItemAtIndex: function(index) {
return this._grid.get_child_at_index(index);
},
@ -525,6 +541,7 @@ const IconGrid = new Lang.Class({
}
}
});
Signals.addSignalMethods(IconGrid.prototype);
const PaginatedIconGrid = new Lang.Class({
Name: 'PaginatedIconGrid',
@ -628,6 +645,10 @@ const PaginatedIconGrid = new Lang.Class({
return this._nPages;
},
getPageHeight: function() {
return this._availableHeightPerPageForItems();
},
getPageY: function(pageNumber) {
if (!this._nPages)
return 0;

View File

@ -23,27 +23,29 @@ const KEYBOARD_TYPE = 'keyboard-type';
const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
const SHOW_KEYBOARD = 'screen-keyboard-enabled';
const CaribouKeyboardIface = <interface name='org.gnome.Caribou.Keyboard'>
<method name='Show'>
<arg type='u' direction='in' />
</method>
<method name='Hide'>
<arg type='u' direction='in' />
</method>
<method name='SetCursorLocation'>
<arg type='i' direction='in' />
<arg type='i' direction='in' />
<arg type='i' direction='in' />
<arg type='i' direction='in' />
</method>
<method name='SetEntryLocation'>
<arg type='i' direction='in' />
<arg type='i' direction='in' />
<arg type='i' direction='in' />
<arg type='i' direction='in' />
</method>
<property name='Name' access='read' type='s' />
</interface>;
const CaribouKeyboardIface = '<node> \
<interface name="org.gnome.Caribou.Keyboard"> \
<method name="Show"> \
<arg type="u" direction="in" /> \
</method> \
<method name="Hide"> \
<arg type="u" direction="in" /> \
</method> \
<method name="SetCursorLocation"> \
<arg type="i" direction="in" /> \
<arg type="i" direction="in" /> \
<arg type="i" direction="in" /> \
<arg type="i" direction="in" /> \
</method> \
<method name="SetEntryLocation"> \
<arg type="i" direction="in" /> \
<arg type="i" direction="in" /> \
<arg type="i" direction="in" /> \
<arg type="i" direction="in" /> \
</method> \
<property name="Name" access="read" type="s" /> \
</interface> \
</node>';
const Key = new Lang.Class({
Name: 'Key',
@ -80,8 +82,16 @@ const Key = new Lang.Class({
style_class: 'keyboard-key' });
button.key_width = this._key.width;
button.connect('button-press-event', Lang.bind(this, function () { this._key.press(); }));
button.connect('button-release-event', Lang.bind(this, function () { this._key.release(); }));
button.connect('button-press-event', Lang.bind(this,
function () {
this._key.press();
return Clutter.EVENT_PROPAGATE;
}));
button.connect('button-release-event', Lang.bind(this,
function () {
this._key.release();
return Clutter.EVENT_PROPAGATE;
}));
return button;
},
@ -104,8 +114,16 @@ const Key = new Lang.Class({
let label = this._getUnichar(extended_key);
let key = new St.Button({ label: label, style_class: 'keyboard-key' });
key.extended_key = extended_key;
key.connect('button-press-event', Lang.bind(this, function () { extended_key.press(); }));
key.connect('button-release-event', Lang.bind(this, function () { extended_key.release(); }));
key.connect('button-press-event', Lang.bind(this,
function () {
extended_key.press();
return Clutter.EVENT_PROPAGATE;
}));
key.connect('button-release-event', Lang.bind(this,
function () {
extended_key.release();
return Clutter.EVENT_PROPAGATE;
}));
this._extended_keyboard.add(key);
}
this._boxPointer.bin.add_actor(this._extended_keyboard);
@ -248,9 +266,14 @@ const Keyboard = new Lang.Class({
return;
}
if (!this._showIdleId)
this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE,
Lang.bind(this, function() { this.Show(time); }));
if (!this._showIdleId) {
this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE,
Lang.bind(this, function() {
this.Show(time);
return GLib.SOURCE_REMOVE;
}));
GLib.Source.set_name_by_id(this._showIdleId, '[gnome-shell] this.Show');
}
},
_createLayersForGroup: function (gname) {
@ -292,7 +315,7 @@ const Keyboard = new Lang.Class({
else if (release && this._capturedPress)
this._hideSubkeys();
return true;
return Clutter.EVENT_STOP;
},
_addRows : function (keys, layout) {
@ -436,7 +459,6 @@ const Keyboard = new Lang.Class({
_createSource: function () {
if (this._source == null) {
this._source = new KeyboardSource(this);
this._source.setTransient(true);
Main.messageTray.add(this._source);
}
},
@ -478,7 +500,9 @@ const Keyboard = new Lang.Class({
Lang.bind(this, function() {
this._clearKeyboardRestTimer();
this._show(monitor);
return GLib.SOURCE_REMOVE;
}));
GLib.Source.set_name_by_id(this._keyboardRestingId, '[gnome-shell] this._clearKeyboardRestTimer');
},
_show: function(monitor) {
@ -503,7 +527,9 @@ const Keyboard = new Lang.Class({
Lang.bind(this, function() {
this._clearKeyboardRestTimer();
this._hide();
return GLib.SOURCE_REMOVE;
}));
GLib.Source.set_name_by_id(this._keyboardRestingId, '[gnome-shell] this._clearKeyboardRestTimer');
},
_hide: function() {

View File

@ -4,7 +4,6 @@ const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
@ -23,11 +22,6 @@ const KEYBOARD_ANIMATION_TIME = 0.15;
const BACKGROUND_FADE_ANIMATION_TIME = 1.0;
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
// The message tray takes this much pressure
// in the pressure barrier at once to release it.
const MESSAGE_TRAY_PRESSURE_THRESHOLD = 250; // pixels
const MESSAGE_TRAY_PRESSURE_TIMEOUT = 1000; // ms
const HOT_CORNER_PRESSURE_THRESHOLD = 100; // pixels
const HOT_CORNER_PRESSURE_TIMEOUT = 1000; // ms
@ -151,7 +145,6 @@ const LayoutManager = new Lang.Class({
this._keyboardIndex = -1;
this._rightPanelBarrier = null;
this._trayBarrier = null;
this._inOverview = false;
this._updateRegionIdle = 0;
@ -211,7 +204,10 @@ const LayoutManager = new Lang.Class({
this.trayBox = new St.Widget({ name: 'trayBox',
layout_manager: new Clutter.BinLayout() });
this.addChrome(this.trayBox);
this._setupTrayPressure();
this.modalDialogGroup = new St.Widget({ name: 'modalDialogGroup',
layout_manager: new Clutter.BinLayout() });
this.uiGroup.add_actor(this.modalDialogGroup);
this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox',
reactive: true,
@ -219,6 +215,11 @@ const LayoutManager = new Lang.Class({
this.addChrome(this.keyboardBox);
this._keyboardHeightNotifyId = 0;
// A dummy actor that tracks the mouse or text cursor, based on the
// position and size set in setDummyCursorGeometry.
this.dummyCursor = new St.Widget({ width: 0, height: 0 });
this.uiGroup.add_actor(this.dummyCursor);
global.stage.remove_actor(global.top_window_group);
this.uiGroup.add_actor(global.top_window_group);
@ -251,7 +252,7 @@ const LayoutManager = new Lang.Class({
this._inOverview = true;
this._updateVisibility();
this._queueUpdateRegions();
this._updateRegions();
},
hideOverview: function() {
@ -352,26 +353,26 @@ const LayoutManager = new Lang.Class({
this.emit('hot-corners-changed');
},
_createBackground: function(monitorIndex) {
_addBackgroundMenu: function(bgManager) {
BackgroundMenu.addBackgroundMenu(bgManager.background.actor, this);
},
_createBackgroundManager: function(monitorIndex) {
let bgManager = new Background.BackgroundManager({ container: this._backgroundGroup,
layoutManager: this,
monitorIndex: monitorIndex });
BackgroundMenu.addBackgroundMenu(bgManager.background.actor);
bgManager.connect('changed', Lang.bind(this, function() {
BackgroundMenu.addBackgroundMenu(bgManager.background.actor);
}));
bgManager.connect('changed', Lang.bind(this, this._addBackgroundMenu));
this._addBackgroundMenu(bgManager);
this._bgManagers.push(bgManager);
return bgManager.background;
return bgManager;
},
_createSecondaryBackgrounds: function() {
_showSecondaryBackgrounds: function() {
for (let i = 0; i < this.monitors.length; i++) {
if (i != this.primaryIndex) {
let background = this._createBackground(i);
let background = this._bgManagers[i].background;
background.actor.show();
background.actor.opacity = 0;
Tweener.addTween(background.actor,
{ opacity: 255,
@ -381,10 +382,6 @@ const LayoutManager = new Lang.Class({
}
},
_createPrimaryBackground: function() {
this._createBackground(this.primaryIndex);
},
_updateBackgrounds: function() {
let i;
for (i = 0; i < this._bgManagers.length; i++)
@ -395,11 +392,12 @@ const LayoutManager = new Lang.Class({
if (Main.sessionMode.isGreeter)
return;
if (this._startingUp)
return;
for (let i = 0; i < this.monitors.length; i++) {
this._createBackground(i);
let bgManager = this._createBackgroundManager(i);
this._bgManagers.push(bgManager);
if (i != this.primaryIndex && this._startingUp)
bgManager.background.actor.hide();
}
},
@ -444,50 +442,9 @@ const LayoutManager = new Lang.Class({
}
},
_setupTrayPressure: function() {
this._trayPressure = new PressureBarrier(MESSAGE_TRAY_PRESSURE_THRESHOLD,
MESSAGE_TRAY_PRESSURE_TIMEOUT,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW);
this._trayPressure.setEventFilter(this._trayBarrierEventFilter);
this._trayPressure.connect('trigger', function(barrier) {
if (Main.layoutManager.bottomMonitor.inFullscreen)
return;
Main.messageTray.openTray();
});
},
_updateTrayBarrier: function() {
let monitor = this.bottomMonitor;
if (this._trayBarrier) {
this._trayPressure.removeBarrier(this._trayBarrier);
this._trayBarrier.destroy();
this._trayBarrier = null;
}
this._trayBarrier = new Meta.Barrier({ display: global.display,
x1: monitor.x, x2: monitor.x + monitor.width,
y1: monitor.y + monitor.height, y2: monitor.y + monitor.height,
directions: Meta.BarrierDirection.NEGATIVE_Y });
this._trayPressure.addBarrier(this._trayBarrier);
},
_trayBarrierEventFilter: function(event) {
// Throw out all events where the pointer was grabbed by another
// client, as the client that grabbed the pointer expects to have
// complete control over it
if (event.grabbed && Main.modalCount == 0)
return true;
return false;
},
_monitorsChanged: function() {
this._updateMonitors();
this._updateBoxes();
this._updateTrayBarrier();
this._updateHotCorners();
this._updateBackgrounds();
this._updateFullscreen();
@ -595,10 +552,10 @@ const LayoutManager = new Lang.Class({
if (Main.sessionMode.isGreeter) {
this.panelBox.translation_y = -this.panelBox.height;
} else {
this._createPrimaryBackground();
this._updateBackgrounds();
// We need to force an update of the regions now before we scale
// the UI group to get the coorect allocation for the struts.
// the UI group to get the correct allocation for the struts.
this._updateRegions();
this.trayBox.hide();
@ -610,7 +567,7 @@ const LayoutManager = new Lang.Class({
this.uiGroup.set_pivot_point(x / global.screen_width,
y / global.screen_height);
this.uiGroup.scale_x = this.uiGroup.scale_y = 0.5;
this.uiGroup.scale_x = this.uiGroup.scale_y = 0.75;
this.uiGroup.opacity = 0;
global.window_group.set_clip(monitor.x, monitor.y, monitor.width, monitor.height);
}
@ -623,10 +580,11 @@ const LayoutManager = new Lang.Class({
// until the event loop is uncontended and idle.
// This helps to prevent us from running the animation
// when the system is bogged down
GLib.idle_add(GLib.PRIORITY_LOW, Lang.bind(this, function() {
let id = GLib.idle_add(GLib.PRIORITY_LOW, Lang.bind(this, function() {
this._startupAnimation();
return false;
return GLib.SOURCE_REMOVE;
}));
GLib.Source.set_name_by_id(id, '[gnome-shell] this._startupAnimation');
},
_startupAnimation: function() {
@ -673,7 +631,7 @@ const LayoutManager = new Lang.Class({
this.keyboardBox.show();
if (!Main.sessionMode.isGreeter) {
this._createSecondaryBackgrounds();
this._showSecondaryBackgrounds();
global.window_group.remove_clip();
}
@ -723,6 +681,21 @@ const LayoutManager = new Lang.Class({
this._updateRegions();
},
// setDummyCursorGeometry:
//
// The cursor dummy is a standard widget commonly used for popup
// menus and box pointers to track, as the box pointer API only
// tracks actors. If you want to pop up a menu based on where the
// user clicked, or where the text cursor is, the cursor dummy
// is what you should use. Given that the menu should not track
// the actual mouse pointer as it moves, you need to call this
// function before you show the menu to ensure it is at the right
// position and has the right size.
setDummyCursorGeometry: function(x, y, w, h) {
this.dummyCursor.set_position(Math.round(x), Math.round(y));
this.dummyCursor.set_size(Math.round(w), Math.round(h));
},
// addChrome:
// @actor: an actor to add to the chrome
// @params: (optional) additional params
@ -814,13 +787,12 @@ const LayoutManager = new Lang.Class({
let actorData = Params.parse(params, defaultParams);
actorData.actor = actor;
actorData.isToplevel = actor.get_parent() == this.uiGroup;
actorData.visibleId = actor.connect('notify::visible',
Lang.bind(this, this._queueUpdateRegions));
actorData.allocationId = actor.connect('notify::allocation',
Lang.bind(this, this._queueUpdateRegions));
actorData.parentSetId = actor.connect('parent-set',
Lang.bind(this, this._actorReparented));
actorData.destroyId = actor.connect('destroy',
Lang.bind(this, this._untrackActor));
// Note that destroying actor will unset its parent, so we don't
// need to connect to 'destroy' too.
@ -838,22 +810,11 @@ const LayoutManager = new Lang.Class({
this._trackedActors.splice(i, 1);
actor.disconnect(actorData.visibleId);
actor.disconnect(actorData.allocationId);
actor.disconnect(actorData.parentSetId);
actor.disconnect(actorData.destroyId);
this._queueUpdateRegions();
},
_actorReparented: function(actor, oldParent) {
let newParent = actor.get_parent();
if (!newParent) {
this._untrackActor(actor);
} else {
let i = this._findActor(actor);
let actorData = this._trackedActors[i];
actorData.isToplevel = (newParent == this.uiGroup);
}
},
_updateVisibility: function() {
let windowsVisible = Main.sessionMode.hasWindows && !this._inOverview;
@ -864,8 +825,6 @@ const LayoutManager = new Lang.Class({
let actorData = this._trackedActors[i], visible;
if (!actorData.trackFullscreen)
continue;
if (!actorData.isToplevel)
continue;
if (!windowsVisible)
visible = true;
@ -905,8 +864,8 @@ const LayoutManager = new Lang.Class({
return;
if (!this._updateRegionIdle)
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions),
Meta.PRIORITY_BEFORE_REDRAW);
this._updateRegionIdle = Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
Lang.bind(this, this._updateRegions));
},
_getWindowActorsForWorkspace: function(workspace) {
@ -937,7 +896,7 @@ const LayoutManager = new Lang.Class({
let rects = [], struts = [], i;
if (this._updateRegionIdle) {
Mainloop.source_remove(this._updateRegionIdle);
Meta.later_remove(this._updateRegionIdle);
delete this._updateRegionIdle;
}
@ -1011,19 +970,39 @@ const LayoutManager = new Lang.Class({
continue;
// Ensure that the strut rects goes all the way to the screen edge,
// as this really what mutter expects.
// as this really what mutter expects. However skip this step
// in cases where this would render an entire monitor unusable.
switch (side) {
case Meta.Side.TOP:
y1 = 0;
let hasMonitorsAbove = this.monitors.some(Lang.bind(this,
function(mon) {
return this._isAboveOrBelowPrimary(mon) &&
mon.y < primary.y;
}));
if (!hasMonitorsAbove)
y1 = 0;
break;
case Meta.Side.BOTTOM:
y2 = global.screen_height;
if (this.primaryIndex == this.bottomIndex)
y2 = global.screen_height;
break;
case Meta.Side.LEFT:
x1 = 0;
let hasMonitorsLeft = this.monitors.some(Lang.bind(this,
function(mon) {
return !this._isAboveOrBelowPrimary(mon) &&
mon.x < primary.x;
}));
if (!hasMonitorsLeft)
x1 = 0;
break;
case Meta.Side.RIGHT:
x2 = global.screen_width;
let hasMonitorsRight = this.monitors.some(Lang.bind(this,
function(mon) {
return !this._isAboveOrBelowPrimary(mon) &&
mon.x > primary.x;
}));
if (!hasMonitorsRight)
x2 = global.screen_width;
break;
}
@ -1042,7 +1021,7 @@ const LayoutManager = new Lang.Class({
workspace.set_builtin_struts(struts);
}
return false;
return GLib.SOURCE_REMOVE;
}
});
Signals.addSignalMethods(LayoutManager.prototype);
@ -1069,10 +1048,10 @@ const HotCorner = new Lang.Class({
this._setupFallbackCornerIfNeeded(layoutManager);
this._pressureBarrier = new PressureBarrier(HOT_CORNER_PRESSURE_THRESHOLD,
HOT_CORNER_PRESSURE_TIMEOUT,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW);
this._pressureBarrier = new TriggerablePressureBarrier(HOT_CORNER_PRESSURE_THRESHOLD,
HOT_CORNER_PRESSURE_TIMEOUT,
Shell.KeyBindingMode.NORMAL |
Shell.KeyBindingMode.OVERVIEW);
this._pressureBarrier.connect('trigger', Lang.bind(this, this._toggleOverview));
// Cache the three ripples instead of dynamically creating and destroying them.
@ -1229,20 +1208,20 @@ const HotCorner = new Lang.Class({
this._entered = true;
this._toggleOverview();
}
return false;
return Clutter.EVENT_PROPAGATE;
},
_onCornerLeft : function(actor, event) {
if (event.get_related() != this.actor)
this._entered = false;
// Consume event, otherwise this will confuse onEnvironsLeft
return true;
return Clutter.EVENT_STOP;
},
_onEnvironsLeft : function(actor, event) {
if (event.get_related() != this._corner)
this._entered = false;
return false;
return Clutter.EVENT_PROPAGATE;
}
});
@ -1250,14 +1229,12 @@ const PressureBarrier = new Lang.Class({
Name: 'PressureBarrier',
_init: function(threshold, timeout, keybindingMode) {
this._threshold = threshold;
this._timeout = timeout;
this.threshold = threshold;
this.timeout = timeout;
this._keybindingMode = keybindingMode;
this._barriers = [];
this._eventFilter = null;
this._isTriggered = false;
this._reset();
this.reset();
},
addBarrier: function(barrier) {
@ -1286,10 +1263,10 @@ const PressureBarrier = new Lang.Class({
this._eventFilter = filter;
},
_reset: function() {
reset: function() {
this._barrierEvents = [];
this._currentPressure = 0;
this._lastTime = 0;
this.currentPressure = 0;
},
_isHorizontal: function(barrier) {
@ -1310,12 +1287,21 @@ const PressureBarrier = new Lang.Class({
return Math.abs(event.dy);
},
get currentPressure() {
return this._currentPressure;
},
set currentPressure(value) {
this._currentPressure = value;
this.emit('pressure-changed');
},
_trimBarrierEvents: function() {
// Events are guaranteed to be sorted in time order from
// oldest to newest, so just look for the first old event,
// and then chop events after that off.
let i = 0;
let threshold = this._lastTime - this._timeout;
let threshold = this._lastTime - this.timeout;
while (i < this._barrierEvents.length) {
let [time, distance] = this._barrierEvents[i];
@ -1328,21 +1314,75 @@ const PressureBarrier = new Lang.Class({
for (i = 0; i < firstNewEvent; i++) {
let [time, distance] = this._barrierEvents[i];
this._currentPressure -= distance;
this.currentPressure = distance;
}
this._barrierEvents = this._barrierEvents.slice(firstNewEvent);
},
_onBarrierLeft: function(barrier, event) {
this._reset();
this.reset();
},
_shouldUseEvent: function(barrier, event) {
if (this._eventFilter && this._eventFilter(event))
return false;
// Throw out all events not in the proper keybinding mode
if (!(this._keybindingMode & Main.keybindingMode))
return false;
let slide = this._getDistanceAlongBarrier(barrier, event);
let distance = this._getDistanceAcrossBarrier(barrier, event);
// Throw out events where the cursor is move more
// along the axis of the barrier than moving with
// the barrier.
if (slide > distance)
return false;
return true;
},
_appendEvent: function(barrier, event) {
let distance = this._getDistanceAcrossBarrier(barrier, event);
this._lastTime = event.time;
this._trimBarrierEvents();
distance = Math.min(15, distance);
this._barrierEvents.push([event.time, distance]);
this.currentPressure += distance;
},
_onBarrierHit: function(barrier, event) {
if (!this._shouldUseEvent(barrier, event))
return;
this._appendEvent(barrier, event);
}
});
Signals.addSignalMethods(PressureBarrier.prototype);
const TriggerablePressureBarrier = new Lang.Class({
Name: 'TriggerablePressureBarrier',
Extends: PressureBarrier,
_init: function(threshold, timeout, keybindingMode) {
this.parent(threshold, timeout, keybindingMode);
this._isTriggered = false;
},
_trigger: function() {
this._isTriggered = true;
this.emit('trigger');
this._reset();
this.reset();
},
_onBarrierLeft: function() {
this.parent();
this._isTriggered = false;
},
_onBarrierHit: function(barrier, event) {
@ -1351,37 +1391,17 @@ const PressureBarrier = new Lang.Class({
if (this._isTriggered)
return;
if (this._eventFilter && this._eventFilter(event))
if (!this._shouldUseEvent(barrier, event))
return;
// Throw out all events not in the proper keybinding mode
if (!(this._keybindingMode & Main.keybindingMode))
return;
let slide = this._getDistanceAlongBarrier(barrier, event);
let distance = this._getDistanceAcrossBarrier(barrier, event);
if (distance >= this._threshold) {
if (distance >= this.threshold) {
this._trigger();
return;
}
// Throw out events where the cursor is move more
// along the axis of the barrier than moving with
// the barrier.
if (slide > distance)
return;
this._appendEvent(barrier, event);
this._lastTime = event.time;
this._trimBarrierEvents();
distance = Math.min(15, distance);
this._barrierEvents.push([event.time, distance]);
this._currentPressure += distance;
if (this._currentPressure >= this._threshold)
if (this.currentPressure >= this.threshold)
this._trigger();
}
},
});
Signals.addSignalMethods(PressureBarrier.prototype);

View File

@ -5,11 +5,67 @@ const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Signals = imports.signals;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Params = imports.misc.params;
const Tweener = imports.ui.tweener;
const DEFAULT_FADE_FACTOR = 0.4;
const VIGNETTE_BRIGHTNESS = 0.8;
const VIGNETTE_SHARPNESS = 0.7;
const VIGNETTE_DECLARATIONS = '\
uniform float brightness;\n\
uniform float vignette_sharpness;\n';
const VIGNETTE_CODE = '\
cogl_color_out.a = cogl_color_in.a;\n\
cogl_color_out.rgb = vec3(0.0, 0.0, 0.0);\n\
vec2 position = cogl_tex_coord_in[0].xy - 0.5;\n\
float t = length(2.0 * position);\n\
t = clamp(t, 0.0, 1.0);\n\
float pixel_brightness = mix(1.0, 1.0 - vignette_sharpness, t);\n\
cogl_color_out.a = cogl_color_out.a * (1 - pixel_brightness * brightness);';
const RadialShaderQuad = new Lang.Class({
Name: 'RadialShaderQuad',
Extends: Shell.GLSLQuad,
_init: function(params) {
this.parent(params);
this._brightnessLocation = this.get_uniform_location('brightness');
this._sharpnessLocation = this.get_uniform_location('vignette_sharpness');
this.brightness = 1.0;
this.vignetteSharpness = 0.0;
},
vfunc_build_pipeline: function() {
this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT,
VIGNETTE_DECLARATIONS, VIGNETTE_CODE, true);
},
get brightness() {
return this._brightness;
},
set brightness(v) {
this._brightness = v;
this.set_uniform_float(this._brightnessLocation,
1, [this._brightness]);
},
get vignetteSharpness() {
return this._sharpness;
},
set vignetteSharpness(v) {
this._sharpness = v;
this.set_uniform_float(this._sharpnessLocation,
1, [this._sharpness]);
}
});
/**
* Lightbox:
@ -43,15 +99,23 @@ const Lightbox = new Lang.Class({
width: null,
height: null,
fadeFactor: DEFAULT_FADE_FACTOR,
radialEffect: false,
});
this._container = container;
this._children = container.get_children();
this._fadeFactor = params.fadeFactor;
this.actor = new St.Bin({ x: 0,
y: 0,
style_class: 'lightbox',
reactive: params.inhibitEvents });
this._radialEffect = params.radialEffect;
if (params.radialEffect)
this.actor = new RadialShaderQuad({ x: 0,
y: 0,
reactive: params.inhibitEvents });
else
this.actor = new St.Bin({ x: 0,
y: 0,
opacity: 0,
style_class: 'lightbox',
reactive: params.inhibitEvents });
container.add_actor(this.actor);
this.actor.raise_top();
@ -101,9 +165,18 @@ const Lightbox = new Lang.Class({
fadeInTime = fadeInTime || 0;
Tweener.removeTweens(this.actor);
if (fadeInTime != 0) {
this.shown = false;
this.actor.opacity = 0;
if (this._radialEffect) {
Tweener.addTween(this.actor,
{ brightness: VIGNETTE_BRIGHTNESS,
vignetteSharpness: VIGNETTE_SHARPNESS,
time: fadeInTime,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.shown = true;
this.emit('shown');
})
});
} else {
Tweener.addTween(this.actor,
{ opacity: 255 * this._fadeFactor,
time: fadeInTime,
@ -113,11 +186,8 @@ const Lightbox = new Lang.Class({
this.emit('shown');
})
});
} else {
this.actor.opacity = 255 * this._fadeFactor;
this.shown = true;
this.emit('shown');
}
this.actor.show();
},
@ -126,7 +196,18 @@ const Lightbox = new Lang.Class({
this.shown = false;
Tweener.removeTweens(this.actor);
if (fadeOutTime != 0) {
if (this._radialEffect) {
Tweener.addTween(this.actor,
{ brightness: 1.0,
vignetteSharpness: 0.0,
opacity: 0,
time: fadeOutTime,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.actor.hide();
})
});
} else {
Tweener.addTween(this.actor,
{ opacity: 0,
time: fadeOutTime,
@ -135,8 +216,6 @@ const Lightbox = new Lang.Class({
this.actor.hide();
})
});
} else {
this.actor.hide();
}
},

View File

@ -27,6 +27,8 @@ const CHEVRON = '>>> ';
/* Imports...feel free to add here as needed */
var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
'const GLib = imports.gi.GLib; ' +
'const GObject = imports.gi.GObject; ' +
'const Gio = imports.gi.Gio; ' +
'const Gtk = imports.gi.Gtk; ' +
'const Mainloop = imports.mainloop; ' +
'const Meta = imports.gi.Meta; ' +
@ -109,6 +111,7 @@ const AutoComplete = new Lang.Class({
}
this._lastTabTime = currTime;
}
return Clutter.EVENT_PROPAGATE;
},
// Insert characters of text not already included in head at cursor position. i.e., if text="abc" and head="a",
@ -558,7 +561,7 @@ const Inspector = new Lang.Class({
_onKeyPressEvent: function (actor, event) {
if (event.get_key_symbol() == Clutter.Escape)
this._close();
return true;
return Clutter.EVENT_STOP;
},
_onButtonPressEvent: function (actor, event) {
@ -567,7 +570,7 @@ const Inspector = new Lang.Class({
this.emit('target', this._target, stageX, stageY);
}
this._close();
return true;
return Clutter.EVENT_STOP;
},
_onScrollEvent: function (actor, event) {
@ -601,12 +604,12 @@ const Inspector = new Lang.Class({
default:
break;
}
return true;
return Clutter.EVENT_STOP;
},
_onMotionEvent: function (actor, event) {
this._update(event);
return true;
return Clutter.EVENT_STOP;
},
_update: function(event) {
@ -629,55 +632,6 @@ const Inspector = new Lang.Class({
Signals.addSignalMethods(Inspector.prototype);
const Memory = new Lang.Class({
Name: 'Memory',
_init: function() {
this.actor = new St.BoxLayout({ vertical: true });
this._glibc_uordblks = new St.Label();
this.actor.add(this._glibc_uordblks);
this._js_bytes = new St.Label();
this.actor.add(this._js_bytes);
this._gjs_boxed = new St.Label();
this.actor.add(this._gjs_boxed);
this._gjs_gobject = new St.Label();
this.actor.add(this._gjs_gobject);
this._gjs_function = new St.Label();
this.actor.add(this._gjs_function);
this._gjs_closure = new St.Label();
this.actor.add(this._gjs_closure);
this._last_gc_seconds_ago = new St.Label();
this.actor.add(this._last_gc_seconds_ago);
this._gcbutton = new St.Button({ label: 'Full GC',
style_class: 'lg-obj-inspector-button' });
this._gcbutton.connect('clicked', Lang.bind(this, function () { System.gc(); this._renderText(); }));
this.actor.add(this._gcbutton, { x_align: St.Align.START,
x_fill: false });
this.actor.connect('notify::mapped', Lang.bind(this, this._renderText));
},
_renderText: function() {
if (!this.actor.mapped)
return;
let memInfo = global.get_memory_info();
this._glibc_uordblks.text = 'glibc_uordblks: ' + memInfo.glibc_uordblks;
this._js_bytes.text = 'js bytes: ' + memInfo.js_bytes;
this._gjs_boxed.text = 'gjs_boxed: ' + memInfo.gjs_boxed;
this._gjs_gobject.text = 'gjs_gobject: ' + memInfo.gjs_gobject;
this._gjs_function.text = 'gjs_function: ' + memInfo.gjs_function;
this._gjs_closure.text = 'gjs_closure: ' + memInfo.gjs_closure;
this._last_gc_seconds_ago.text = 'last_gc_seconds_ago: ' + memInfo.last_gc_seconds_ago;
}
});
const Extensions = new Lang.Class({
Name: 'Extensions',
@ -718,13 +672,13 @@ const Extensions = new Lang.Class({
_onViewSource: function (actor) {
let extension = actor._extension;
let uri = extension.dir.get_uri();
Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context());
Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context(0, -1));
this._lookingGlass.close();
},
_onWebPage: function (actor) {
let extension = actor._extension;
Gio.app_info_launch_default_for_uri(extension.metadata.url, global.create_app_launch_context());
Gio.app_info_launch_default_for_uri(extension.metadata.url, global.create_app_launch_context(0, -1));
this._lookingGlass.close();
},
@ -877,7 +831,23 @@ const LookingGlass = new Lang.Class({
global.stage.set_key_focus(this._entry);
}));
this.actor.hide();
return true;
return Clutter.EVENT_STOP;
}));
let gcIcon = new St.Icon({ icon_name: 'gnome-fs-trash-full',
icon_size: 24 });
toolbar.add_actor(gcIcon);
gcIcon.reactive = true;
gcIcon.connect('button-press-event', Lang.bind(this, function () {
gcIcon.icon_name = 'gnome-fs-trash-empty';
System.gc();
this._timeoutId = Mainloop.timeout_add(500, Lang.bind(this, function () {
gcIcon.icon_name = 'gnome-fs-trash-full';
this._timeoutId = 0;
return GLib.SOURCE_REMOVE;
}));
GLib.Source.set_name_by_id(this._timeoutId, '[gnome-shell] gcIcon.icon_name = \'gnome-fs-trash-full\'');
return Clutter.EVENT_PROPAGATE;
}));
let notebook = new Notebook();
@ -907,9 +877,6 @@ const LookingGlass = new Lang.Class({
this._windowList = new WindowList(this);
notebook.appendPage('Windows', this._windowList.actor);
this._memory = new Memory();
notebook.appendPage('Memory', this._memory.actor);
this._extensions = new Extensions(this);
notebook.appendPage('Extensions', this._extensions.actor);
@ -1102,7 +1069,7 @@ const LookingGlass = new Lang.Class({
} else {
this.close();
}
return true;
return Clutter.EVENT_STOP;
}
// Ctrl+PgUp and Ctrl+PgDown switches tabs in the notebook view
if (modifierState & Clutter.ModifierType.CONTROL_MASK) {
@ -1112,7 +1079,7 @@ const LookingGlass = new Lang.Class({
this._notebook.nextTab();
}
}
return false;
return Clutter.EVENT_PROPAGATE;
},
open : function() {

View File

@ -11,6 +11,7 @@ const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Signals = imports.signals;
const Background = imports.ui.background;
const FocusCaretTracker = imports.ui.focusCaretTracker;
const Main = imports.ui.main;
const MagnifierDBus = imports.ui.magnifierDBus;
@ -57,20 +58,6 @@ const Magnifier = new Lang.Class({
// Magnifier is a manager of ZoomRegions.
this._zoomRegions = [];
// Export to dbus.
magDBusService = new MagnifierDBus.ShellMagnifier();
let showAtLaunch = this._settingsInit();
this.setActive(showAtLaunch);
},
_initialize: function() {
if (this._initialized)
return;
this._initialized = true;
this._settingsInitLate();
// Create small clutter tree for the magnified mouse.
let cursorTracker = Meta.CursorTracker.get_for_screen(global.screen);
this._mouseSprite = new Clutter.Texture();
@ -86,11 +73,15 @@ const Magnifier = new Lang.Class({
let aZoomRegion = new ZoomRegion(this, this._cursorRoot);
this._zoomRegions.push(aZoomRegion);
this._settingsInitRegion(aZoomRegion);
let showAtLaunch = this._settingsInit(aZoomRegion);
aZoomRegion.scrollContentsTo(this.xMouse, this.yMouse);
cursorTracker.connect('cursor-changed', Lang.bind(this, this._updateMouseSprite));
this._cursorTracker = cursorTracker;
// Export to dbus.
magDBusService = new MagnifierDBus.ShellMagnifier();
this.setActive(showAtLaunch);
},
/**
@ -115,20 +106,21 @@ const Magnifier = new Lang.Class({
* @activate: Boolean to activate or de-activate the magnifier.
*/
setActive: function(activate) {
if (activate == this.isActive())
return;
if (activate)
this._initialize();
let isActive = this.isActive();
this._zoomRegions.forEach (function(zoomRegion, index, array) {
zoomRegion.setActive(activate);
});
if (activate)
this.startTrackingMouse();
else
this.stopTrackingMouse();
if (isActive != activate) {
if (activate) {
Meta.disable_unredirect_for_screen(global.screen);
this.startTrackingMouse();
} else {
Meta.enable_unredirect_for_screen(global.screen);
this.stopTrackingMouse();
}
}
// Make sure system mouse pointer is shown when all zoom regions are
// invisible.
@ -448,68 +440,64 @@ const Magnifier = new Lang.Class({
this._mouseSprite.set_anchor_point(xHot, yHot);
},
_settingsInitRegion: function(zoomRegion) {
// Mag factor is accurate to two decimal places.
let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2));
if (aPref != 0.0)
zoomRegion.setMagFactor(aPref, aPref);
aPref = this._settings.get_enum(SCREEN_POSITION_KEY);
if (aPref)
zoomRegion.setScreenPosition(aPref);
zoomRegion.setLensMode(this._settings.get_boolean(LENS_MODE_KEY));
zoomRegion.setClampScrollingAtEdges(!this._settings.get_boolean(CLAMP_MODE_KEY));
aPref = this._settings.get_enum(MOUSE_TRACKING_KEY);
if (aPref)
zoomRegion.setMouseTrackingMode(aPref);
aPref = this._settings.get_enum(FOCUS_TRACKING_KEY);
if (aPref)
zoomRegion.setFocusTrackingMode(aPref);
aPref = this._settings.get_enum(CARET_TRACKING_KEY);
if (aPref)
zoomRegion.setCaretTrackingMode(aPref);
aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY);
if (aPref)
zoomRegion.setInvertLightness(aPref);
aPref = this._settings.get_double(COLOR_SATURATION_KEY);
if (aPref)
zoomRegion.setColorSaturation(aPref);
let bc = {};
bc.r = this._settings.get_double(BRIGHT_RED_KEY);
bc.g = this._settings.get_double(BRIGHT_GREEN_KEY);
bc.b = this._settings.get_double(BRIGHT_BLUE_KEY);
zoomRegion.setBrightness(bc);
bc.r = this._settings.get_double(CONTRAST_RED_KEY);
bc.g = this._settings.get_double(CONTRAST_GREEN_KEY);
bc.b = this._settings.get_double(CONTRAST_BLUE_KEY);
zoomRegion.setContrast(bc);
},
_settingsInit: function() {
_settingsInit: function(zoomRegion) {
this._appSettings = new Gio.Settings({ schema: APPLICATIONS_SCHEMA });
this._settings = new Gio.Settings({ schema: MAGNIFIER_SCHEMA });
this._appSettings.connect('changed::' + SHOW_KEY, Lang.bind(this, function() {
let active = this._appSettings.get_boolean(SHOW_KEY);
this.setActive(active);
}));
if (zoomRegion) {
// Mag factor is accurate to two decimal places.
let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2));
if (aPref != 0.0)
zoomRegion.setMagFactor(aPref, aPref);
return this._appSettings.get_boolean(SHOW_KEY);
},
aPref = this._settings.get_enum(SCREEN_POSITION_KEY);
if (aPref)
zoomRegion.setScreenPosition(aPref);
zoomRegion.setLensMode(this._settings.get_boolean(LENS_MODE_KEY));
zoomRegion.setClampScrollingAtEdges(!this._settings.get_boolean(CLAMP_MODE_KEY));
aPref = this._settings.get_enum(MOUSE_TRACKING_KEY);
if (aPref)
zoomRegion.setMouseTrackingMode(aPref);
aPref = this._settings.get_enum(FOCUS_TRACKING_KEY);
if (aPref)
zoomRegion.setFocusTrackingMode(aPref);
aPref = this._settings.get_enum(CARET_TRACKING_KEY);
if (aPref)
zoomRegion.setCaretTrackingMode(aPref);
aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY);
if (aPref)
zoomRegion.setInvertLightness(aPref);
aPref = this._settings.get_double(COLOR_SATURATION_KEY);
if (aPref)
zoomRegion.setColorSaturation(aPref);
let bc = {};
bc.r = this._settings.get_double(BRIGHT_RED_KEY);
bc.g = this._settings.get_double(BRIGHT_GREEN_KEY);
bc.b = this._settings.get_double(BRIGHT_BLUE_KEY);
zoomRegion.setBrightness(bc);
bc.r = this._settings.get_double(CONTRAST_RED_KEY);
bc.g = this._settings.get_double(CONTRAST_GREEN_KEY);
bc.b = this._settings.get_double(CONTRAST_BLUE_KEY);
zoomRegion.setContrast(bc);
}
_settingsInitLate: function() {
let showCrosshairs = this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY);
this.addCrosshairs();
this.setCrosshairsVisible(showCrosshairs);
this._appSettings.connect('changed::' + SHOW_KEY,
Lang.bind(this, function() {
this.setActive(this._appSettings.get_boolean(SHOW_KEY));
}));
this._settings.connect('changed::' + SCREEN_POSITION_KEY,
Lang.bind(this, this._updateScreenPosition));
this._settings.connect('changed::' + MAG_FACTOR_KEY,
@ -573,6 +561,7 @@ const Magnifier = new Lang.Class({
Lang.bind(this, function() {
this.setCrosshairsClip(this._settings.get_boolean(CROSS_HAIRS_CLIP_KEY));
}));
return this._appSettings.get_boolean(SHOW_KEY);
},
_updateScreenPosition: function() {
@ -731,8 +720,16 @@ const ZoomRegion = new Lang.Class({
let component = event.source.get_component_iface();
if (!component || event.detail1 != 1)
return;
let extents = component.get_extents(Atspi.CoordType.SCREEN);
[this._xFocus, this._yFocus] = [extents.x, extents.y]
let extents;
try {
extents = component.get_extents(Atspi.CoordType.SCREEN);
} catch(e) {
log('Failed to read extents of focused component: ' + e.message);
return;
}
[this._xFocus, this._yFocus] = [extents.x + (extents.width / 2),
extents.y + (extents.height / 2)];
this._centerFromFocusPosition();
},
@ -740,7 +737,14 @@ const ZoomRegion = new Lang.Class({
let text = event.source.get_text_iface();
if (!text)
return;
let extents = text.get_character_extents(text.get_caret_offset(), 0);
let extents;
try {
extents = text.get_character_extents(text.get_caret_offset(), 0);
} catch(e) {
log('Failed to read extents of text caret: ' + e.message);
return;
}
[this._xCaret, this._yCaret] = [extents.x, extents.y];
this._centerFromCaretPosition();
},
@ -763,6 +767,9 @@ const ZoomRegion = new Lang.Class({
} else {
this._destroyActors();
}
this._syncCaretTracking();
this._syncFocusTracking();
},
/**
@ -822,10 +829,7 @@ const ZoomRegion = new Lang.Class({
*/
setFocusTrackingMode: function(mode) {
this._focusTrackingMode = mode;
if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.NONE)
this._focusCaretTracker.deregisterFocusListener();
else
this._focusCaretTracker.registerFocusListener();
this._syncFocusTracking();
},
/**
@ -834,10 +838,27 @@ const ZoomRegion = new Lang.Class({
*/
setCaretTrackingMode: function(mode) {
this._caretTrackingMode = mode;
if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.NONE)
this._focusCaretTracker.deregisterCaretListener();
this._syncCaretTracking();
},
_syncFocusTracking: function() {
let enabled = this._focusTrackingMode != GDesktopEnums.MagnifierFocusTrackingMode.NONE &&
this.isActive();
if (enabled)
this._focusCaretTracker.registerFocusListener();
else
this._focusCaretTracker.deregisterFocusListener();
},
_syncCaretTracking: function() {
let enabled = this._caretTrackingMode != GDesktopEnums.MagnifierCaretTrackingMode.NONE &&
this.isActive();
if (enabled)
this._focusCaretTracker.registerCaretListener();
else
this._focusCaretTracker.deregisterCaretListener();
},
/**
@ -1178,13 +1199,17 @@ const ZoomRegion = new Lang.Class({
// Add a background for when the magnified uiGroup is scrolled
// out of view (don't want to see desktop showing through).
this._background = new Clutter.Actor({ background_color: Main.DEFAULT_BACKGROUND_COLOR,
layout_manager: new Clutter.BinLayout(),
width: global.screen_width,
height: global.screen_height });
let noiseTexture = (new Background.SystemBackground()).actor;
this._background.add_actor(noiseTexture);
mainGroup.add_actor(this._background);
// Clone the group that contains all of UI on the screen. This is the
// chrome, the windows, etc.
this._uiGroupClone = new Clutter.Clone({ source: Main.uiGroup });
this._uiGroupClone = new Clutter.Clone({ source: Main.uiGroup,
clip_to_allocation: true });
mainGroup.add_actor(this._uiGroupClone);
// Add either the given mouseSourceActor to the ZoomRegion, or a clone of

View File

@ -9,85 +9,89 @@ const ZOOM_SERVICE_PATH = '/org/gnome/Magnifier/ZoomRegion';
// Subset of gnome-mag's Magnifier dbus interface -- to be expanded. See:
// http://git.gnome.org/browse/gnome-mag/tree/xml/...Magnifier.xml
const MagnifierIface = <interface name="org.gnome.Magnifier">
<method name="setActive">
<arg type="b" direction="in" />
</method>
<method name="isActive">
<arg type="b" direction="out" />
</method>
<method name="showCursor" />
<method name="hideCursor" />
<method name="createZoomRegion">
<arg type="d" direction="in" />
<arg type="d" direction="in" />
<arg type="ai" direction="in" />
<arg type="ai" direction="in" />
<arg type="o" direction="out" />
</method>
<method name="addZoomRegion">
<arg type="o" direction="in" />
<arg type="b" direction="out" />
</method>
<method name="getZoomRegions">
<arg type="ao" direction="out" />
</method>
<method name="clearAllZoomRegions" />
<method name="fullScreenCapable">
<arg type="b" direction="out" />
</method>
<method name="setCrosswireSize">
<arg type="i" direction="in" />
</method>
<method name="getCrosswireSize">
<arg type="i" direction="out" />
</method>
<method name="setCrosswireLength">
<arg type="i" direction="in" />
</method>
<method name="getCrosswireLength">
<arg type="i" direction="out" />
</method>
<method name="setCrosswireClip">
<arg type="b" direction="in" />
</method>
<method name="getCrosswireClip">
<arg type="b" direction="out" />
</method>
<method name="setCrosswireColor">
<arg type="u" direction="in" />
</method>
<method name="getCrosswireColor">
<arg type="u" direction="out" />
</method>
</interface>;
const MagnifierIface = '<node> \
<interface name="org.gnome.Magnifier"> \
<method name="setActive"> \
<arg type="b" direction="in" /> \
</method> \
<method name="isActive"> \
<arg type="b" direction="out" /> \
</method> \
<method name="showCursor" /> \
<method name="hideCursor" /> \
<method name="createZoomRegion"> \
<arg type="d" direction="in" /> \
<arg type="d" direction="in" /> \
<arg type="ai" direction="in" /> \
<arg type="ai" direction="in" /> \
<arg type="o" direction="out" /> \
</method> \
<method name="addZoomRegion"> \
<arg type="o" direction="in" /> \
<arg type="b" direction="out" /> \
</method> \
<method name="getZoomRegions"> \
<arg type="ao" direction="out" /> \
</method> \
<method name="clearAllZoomRegions" /> \
<method name="fullScreenCapable"> \
<arg type="b" direction="out" /> \
</method> \
<method name="setCrosswireSize"> \
<arg type="i" direction="in" /> \
</method> \
<method name="getCrosswireSize"> \
<arg type="i" direction="out" /> \
</method> \
<method name="setCrosswireLength"> \
<arg type="i" direction="in" /> \
</method> \
<method name="getCrosswireLength"> \
<arg type="i" direction="out" /> \
</method> \
<method name="setCrosswireClip"> \
<arg type="b" direction="in" /> \
</method> \
<method name="getCrosswireClip"> \
<arg type="b" direction="out" /> \
</method> \
<method name="setCrosswireColor"> \
<arg type="u" direction="in" /> \
</method> \
<method name="getCrosswireColor"> \
<arg type="u" direction="out" /> \
</method> \
</interface> \
</node>';
// Subset of gnome-mag's ZoomRegion dbus interface -- to be expanded. See:
// http://git.gnome.org/browse/gnome-mag/tree/xml/...ZoomRegion.xml
const ZoomRegionIface = <interface name="org.gnome.Magnifier.ZoomRegion">
<method name="setMagFactor">
<arg type="d" direction="in" />
<arg type="d" direction="in" />
</method>
<method name="getMagFactor">
<arg type="d" direction="out" />
<arg type="d" direction="out" />
</method>
<method name="setRoi">
<arg type="ai" direction="in" />
</method>
<method name="getRoi">
<arg type="ai" direction="out" />
</method>
<method name="shiftContentsTo">
<arg type="i" direction="in" />
<arg type="i" direction="in" />
<arg type="b" direction="out" />
</method>
<method name="moveResize">
<arg type="ai" direction="in" />
</method>
</interface>;
const ZoomRegionIface = '<node> \
<interface name="org.gnome.Magnifier.ZoomRegion"> \
<method name="setMagFactor"> \
<arg type="d" direction="in" /> \
<arg type="d" direction="in" /> \
</method> \
<method name="getMagFactor"> \
<arg type="d" direction="out" /> \
<arg type="d" direction="out" /> \
</method> \
<method name="setRoi"> \
<arg type="ai" direction="in" /> \
</method> \
<method name="getRoi"> \
<arg type="ai" direction="out" /> \
</method> \
<method name="shiftContentsTo"> \
<arg type="i" direction="in" /> \
<arg type="i" direction="in" /> \
<arg type="b" direction="out" /> \
</method> \
<method name="moveResize"> \
<arg type="ai" direction="in" /> \
</method> \
</interface> \
</node>';
// For making unique ZoomRegion DBus proxy object paths of the form:
// '/org/gnome/Magnifier/ZoomRegion/zoomer0',

View File

@ -43,6 +43,7 @@ const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
const STICKY_KEYS_ENABLE = 'stickykeys-enable';
const GNOMESHELL_STARTED_MESSAGE_ID = 'f3ea493c22934e26811cd62abe8e203a';
let componentManager = null;
let panel = null;
@ -55,7 +56,7 @@ let screenShield = null;
let notificationDaemon = null;
let windowAttentionHandler = null;
let ctrlAltTabManager = null;
let osdWindow = null;
let osdWindowManager = null;
let sessionMode = null;
let shellDBusService = null;
let shellMountOpDBusService = null;
@ -103,20 +104,12 @@ function start() {
global.logError = window.log;
global.log = window.log;
if (!Meta.is_wayland_compositor)
Meta.is_wayland_compositor = function () { return false; };
// 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();
sessionMode.connect('sessions-loaded', _sessionsLoaded);
sessionMode.init();
}
function _sessionsLoaded() {
sessionMode.connect('updated', _sessionUpdated);
_initializePrefs();
_initializeUI();
@ -163,7 +156,7 @@ function _initializeUI() {
screencastService = new Screencast.ScreencastService();
xdndHandler = new XdndHandler.XdndHandler();
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
osdWindow = new OsdWindow.OsdWindow();
osdWindowManager = new OsdWindow.OsdWindowManager();
overview = new Overview.Overview();
wm = new WindowManager.WindowManager();
magnifier = new Magnifier.Magnifier();
@ -196,8 +189,6 @@ function _initializeUI() {
_startDate = new Date();
log('GNOME Shell started at ' + _startDate);
let perfModuleName = GLib.getenv("SHELL_PERF_MODULE");
if (perfModuleName) {
let perfOutput = GLib.getenv("SHELL_PERF_OUTPUT");
@ -221,6 +212,17 @@ function _initializeUI() {
if (screenShield) {
screenShield.lockIfWasLocked();
}
if (LoginManager.haveSystemd() &&
sessionMode.currentMode != 'gdm' &&
sessionMode.currentMode != 'initial-setup') {
// Do not import globally to not depend
// on systemd on non-systemd systems.
let GSystem = imports.gi.GSystem;
GSystem.log_structured_print('GNOME Shell started at ' + _startDate,
['MESSAGE_ID=' + GNOMESHELL_STARTED_MESSAGE_ID]);
} else {
log('GNOME Shell started at ' + _startDate);
}
});
}
@ -614,7 +616,8 @@ function queueDeferredWork(workId) {
_deferredTimeoutId = Mainloop.timeout_add_seconds(DEFERRED_TIMEOUT_SECONDS, function () {
_runAllDeferredWork();
_deferredTimeoutId = 0;
return false;
return GLib.SOURCE_REMOVE;
});
GLib.Source.set_name_by_id(_deferredTimeoutId, '[gnome-shell] _runAllDeferredWork');
}
}

File diff suppressed because it is too large Load Diff

View File

@ -41,7 +41,6 @@ const ModalDialog = new Lang.Class({
_init: function(params) {
params = Params.parse(params, { shellReactive: false,
styleClass: null,
parentActor: Main.uiGroup,
keybindingMode: Shell.KeyBindingMode.SYSTEM_MODAL,
shouldFadeIn: true,
destroyOnClose: true });
@ -57,7 +56,7 @@ const ModalDialog = new Lang.Class({
x: 0,
y: 0,
accessible_role: Atk.Role.DIALOG });
params.parentActor.add_actor(this._group);
Main.layoutManager.modalDialogGroup.add_actor(this._group);
let constraint = new Clutter.BindConstraint({ source: global.stage,
coordinate: Clutter.BindCoordinate.ALL });
@ -89,7 +88,8 @@ const ModalDialog = new Lang.Class({
if (!this._shellReactive) {
this._lightbox = new Lightbox.Lightbox(this._group,
{ inhibitEvents: true });
{ inhibitEvents: true,
radialEffect: true });
this._lightbox.highlight(this._backgroundBin);
this._eventBlocker = new Clutter.Actor({ reactive: true });
@ -229,6 +229,7 @@ const ModalDialog = new Lang.Class({
_onKeyPressEvent: function(object, event) {
this._pressedKey = event.get_key_symbol();
return Clutter.EVENT_PROPAGATE;
},
_onKeyReleaseEvent: function(object, event) {
@ -237,21 +238,21 @@ const ModalDialog = new Lang.Class({
let symbol = event.get_key_symbol();
if (symbol != pressedKey)
return false;
return Clutter.EVENT_PROPAGATE;
let buttonInfo = this._buttonKeys[symbol];
if (!buttonInfo)
return false;
return Clutter.EVENT_PROPAGATE;
let button = buttonInfo['button'];
let action = buttonInfo['action'];
if (action && button.reactive) {
action();
return true;
return Clutter.EVENT_STOP;
}
return false;
return Clutter.EVENT_PROPAGATE;
},
_onGroupDestroy: function() {

View File

@ -4,6 +4,7 @@ const Clutter = imports.gi.Clutter;
const GdkPixbuf = imports.gi.GdkPixbuf;
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 Mainloop = imports.mainloop;
@ -15,54 +16,56 @@ const MessageTray = imports.ui.messageTray;
const Params = imports.misc.params;
const Util = imports.misc.util;
let nextNotificationId = 1;
// Should really be defined in Gio.js
const BusIface = <interface name="org.freedesktop.DBus">
<method name="GetConnectionUnixProcessID">
<arg type="s" direction="in" />
<arg type="u" direction="out" />
</method>
</interface>;
const BusIface = '<node> \
<interface name="org.freedesktop.DBus"> \
<method name="GetConnectionUnixProcessID"> \
<arg type="s" direction="in" /> \
<arg type="u" direction="out" /> \
</method> \
</interface> \
</node>';
var BusProxy = Gio.DBusProxy.makeProxyWrapper(BusIface);
function Bus() {
return new BusProxy(Gio.DBus.session, 'org.freedesktop.DBus', '/org/freedesktop/DBus');
}
const NotificationDaemonIface = <interface name="org.freedesktop.Notifications">
<method name="Notify">
<arg type="s" direction="in"/>
<arg type="u" direction="in"/>
<arg type="s" direction="in"/>
<arg type="s" direction="in"/>
<arg type="s" direction="in"/>
<arg type="as" direction="in"/>
<arg type="a{sv}" direction="in"/>
<arg type="i" direction="in"/>
<arg type="u" direction="out"/>
</method>
<method name="CloseNotification">
<arg type="u" direction="in"/>
</method>
<method name="GetCapabilities">
<arg type="as" direction="out"/>
</method>
<method name="GetServerInformation">
<arg type="s" direction="out"/>
<arg type="s" direction="out"/>
<arg type="s" direction="out"/>
<arg type="s" direction="out"/>
</method>
<signal name="NotificationClosed">
<arg type="u"/>
<arg type="u"/>
</signal>
<signal name="ActionInvoked">
<arg type="u"/>
<arg type="s"/>
</signal>
</interface>;
const FdoNotificationsIface = '<node> \
<interface name="org.freedesktop.Notifications"> \
<method name="Notify"> \
<arg type="s" direction="in"/> \
<arg type="u" direction="in"/> \
<arg type="s" direction="in"/> \
<arg type="s" direction="in"/> \
<arg type="s" direction="in"/> \
<arg type="as" direction="in"/> \
<arg type="a{sv}" direction="in"/> \
<arg type="i" direction="in"/> \
<arg type="u" direction="out"/> \
</method> \
<method name="CloseNotification"> \
<arg type="u" direction="in"/> \
</method> \
<method name="GetCapabilities"> \
<arg type="as" direction="out"/> \
</method> \
<method name="GetServerInformation"> \
<arg type="s" direction="out"/> \
<arg type="s" direction="out"/> \
<arg type="s" direction="out"/> \
<arg type="s" direction="out"/> \
</method> \
<signal name="NotificationClosed"> \
<arg type="u"/> \
<arg type="u"/> \
</signal> \
<signal name="ActionInvoked"> \
<arg type="u"/> \
<arg type="s"/> \
</signal> \
</interface> \
</node>';
const NotificationClosedReason = {
EXPIRED: 1,
@ -88,146 +91,11 @@ const rewriteRules = {
]
};
const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
'bluetooth-applet': 'bluetooth',
'gnome-volume-control-applet': 'volume', // renamed to gnome-sound-applet
// when moved to control center
'gnome-sound-applet': 'volume',
'nm-applet': 'network',
'gnome-power-manager': 'battery',
'keyboard': 'keyboard',
'a11y-keyboard': 'a11y',
'kbd-scrolllock': 'keyboard',
'kbd-numlock': 'keyboard',
'kbd-capslock': 'keyboard',
'ibus-ui-gtk': 'keyboard'
};
const NotificationGenericPolicy = new Lang.Class({
Name: 'NotificationGenericPolicy',
Extends: MessageTray.NotificationPolicy,
const FdoNotificationDaemon = new Lang.Class({
Name: 'FdoNotificationDaemon',
_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',
_init: function() {
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(NotificationDaemonIface, this);
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(FdoNotificationsIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/freedesktop/Notifications');
this._sources = [];
@ -235,16 +103,12 @@ const NotificationDaemon = new Lang.Class({
this._notifications = {};
this._busProxy = new Bus();
this._trayManager = new Shell.TrayManager();
this._trayIconAddedId = this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
this._trayIconRemovedId = this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
this._nextNotificationId = 1;
Shell.WindowTracker.get_default().connect('notify::focus-app',
Lang.bind(this, this._onFocusAppChanged));
Main.overview.connect('hidden',
Lang.bind(this, this._onFocusAppChanged));
this._trayManager.manage_screen(global.screen, Main.messageTray.actor);
},
_imageForNotificationData: function(hints) {
@ -285,30 +149,23 @@ const NotificationDaemon = new Lang.Class({
return null;
},
_lookupSource: function(title, pid, trayIcon) {
_lookupSource: function(title, pid) {
for (let i = 0; i < this._sources.length; i++) {
let source = this._sources[i];
if (source.pid == pid &&
(source.initialTitle == title || source.trayIcon || trayIcon))
if (source.pid == pid && source.initialTitle == title)
return source;
}
return null;
},
// Returns the source associated with ndata.notification if it is set.
// Otherwise, returns the source associated with the title and pid if
// such source is stored in this._sources and the notification is not
// transient. If the existing or requested source is associated with
// a tray icon and passed in pid matches a pid of an existing source,
// the title match is ignored to enable representing a tray icon and
// notifications from the same application with a single source.
//
// If no existing source is found, a new source is created as long as
// pid is provided.
//
// Either a pid or ndata.notification is needed to retrieve or
// create a source.
_getSource: function(title, pid, ndata, sender, trayIcon) {
_getSource: function(title, pid, ndata, sender) {
if (!pid && !(ndata && ndata.notification))
return null;
@ -319,32 +176,20 @@ const NotificationDaemon = new Lang.Class({
if (ndata && ndata.notification)
return ndata.notification.source;
let isForTransientNotification = (ndata && ndata.hints['transient'] == true);
// We don't want to override a persistent notification
// with a transient one from the same sender, so we
// always create a new source object for new transient notifications
// and never add it to this._sources .
if (!isForTransientNotification) {
let source = this._lookupSource(title, pid, trayIcon);
if (source) {
source.setTitle(title);
return source;
}
let source = this._lookupSource(title, pid);
if (source) {
source.setTitle(title);
return source;
}
let source = new Source(title, pid, sender, trayIcon, ndata ? ndata.hints['desktop-entry'] : null);
source.setTransient(isForTransientNotification);
let source = new FdoNotificationDaemonSource(title, pid, sender, ndata ? ndata.hints['desktop-entry'] : null);
if (!isForTransientNotification) {
this._sources.push(source);
source.connect('destroy', Lang.bind(this,
function() {
let index = this._sources.indexOf(source);
if (index >= 0)
this._sources.splice(index, 1);
}));
}
this._sources.push(source);
source.connect('destroy', Lang.bind(this, function() {
let index = this._sources.indexOf(source);
if (index >= 0)
this._sources.splice(index, 1);
}));
Main.messageTray.add(source);
return source;
@ -372,12 +217,13 @@ const NotificationDaemon = new Lang.Class({
hints['category'] == 'presence.offline')) {
// Ignore replacesId since we already sent back a
// NotificationClosed for that id.
id = nextNotificationId++;
Mainloop.idle_add(Lang.bind(this,
function () {
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
return false;
}));
id = this._nextNotificationId++;
let idle_id = Mainloop.idle_add(Lang.bind(this,
function () {
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
return GLib.SOURCE_REMOVE;
}));
GLib.Source.set_name_by_id(idle_id, '[gnome-shell] this._emitNotificationClosed');
return invocation.return_value(GLib.Variant.new('(u)', [id]));
}
@ -396,12 +242,13 @@ const NotificationDaemon = new Lang.Class({
if (!hints['image-path'] && hints['image_path'])
hints['image-path'] = hints['image_path']; // version 1.1 of the spec
if (!hints['image-data'])
if (!hints['image-data']) {
if (hints['image_data'])
hints['image-data'] = hints['image_data']; // version 1.1 of the spec
else if (hints['icon_data'] && !hints['image-path'])
// early versions of the spec; 'icon_data' should only be used if 'image-path' is not available
hints['image-data'] = hints['icon_data'];
}
let ndata = { appName: appName,
icon: icon,
@ -415,7 +262,7 @@ const NotificationDaemon = new Lang.Class({
ndata.notification = this._notifications[replacesId].notification;
} else {
replacesId = 0;
ndata.id = id = nextNotificationId++;
ndata.id = id = this._nextNotificationId++;
}
this._notifications[id] = ndata;
@ -450,26 +297,30 @@ const NotificationDaemon = new Lang.Class({
let [pid] = result;
source = this._getSource(appName, pid, ndata, sender, null);
// We only store sender-pid entries for persistent sources.
// Removing the entries once the source is destroyed
// would result in the entries associated with transient
// sources removed once the notification is shown anyway.
// However, keeping these pairs would mean that we would
// possibly remove an entry associated with a persistent
// source when a transient source for the same sender is
// distroyed.
if (!source.isTransient) {
this._senderToPid[sender] = pid;
source.connect('destroy', Lang.bind(this, function() {
delete this._senderToPid[sender];
}));
}
this._senderToPid[sender] = pid;
source.connect('destroy', Lang.bind(this, function() {
delete this._senderToPid[sender];
}));
this._notifyForSource(source, ndata);
}));
return invocation.return_value(GLib.Variant.new('(u)', [id]));
},
_makeButton: function(id, label, useActionIcons) {
let button = new St.Button({ can_focus: true,
x_expand: true,
style_class: 'notification-button' });
let iconName = id.endsWith('-symbolic') ? id : id + '-symbolic';
if (useActionIcons && Gtk.IconTheme.get_default().has_icon(iconName)) {
button.child = new St.Icon({ icon_name: iconName, icon_size: 16 });
} else {
button.label = label;
}
return button;
},
_notifyForSource: function(source, ndata) {
let [id, icon, summary, body, actions, hints, notification] =
[ndata.id, ndata.icon, ndata.summary, ndata.body,
@ -495,10 +346,6 @@ const NotificationDaemon = new Lang.Class({
}
this._emitNotificationClosed(ndata.id, notificationClosedReason);
}));
notification.connect('action-invoked', Lang.bind(this,
function(n, actionId) {
this._emitActionInvoked(ndata.id, actionId);
}));
}
// Mark music notifications so they can be shown in the screen shield
@ -507,8 +354,6 @@ const NotificationDaemon = new Lang.Class({
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
@ -517,10 +362,7 @@ const NotificationDaemon = new Lang.Class({
// 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)
if (!gicon && gimage)
gicon = gimage;
else if (!gicon)
gicon = this._fallbackIconForNotificationData(hints);
@ -530,20 +372,34 @@ const NotificationDaemon = new Lang.Class({
clear: true,
soundFile: hints['sound-file'],
soundName: hints['sound-name'] });
notification.setImage(image);
let hasDefaultAction = false;
if (actions.length) {
notification.setUseActionIcons(hints['action-icons'] == true);
let useActionIcons = (hints['action-icons'] == true);
for (let i = 0; i < actions.length - 1; i += 2) {
if (actions[i] == 'default')
notification.connect('clicked', Lang.bind(this,
function() {
this._emitActionInvoked(ndata.id, "default");
}));
else
notification.addButton(actions[i], actions[i + 1]);
let [actionId, label] = [actions[i], actions[i+1]];
if (actionId == 'default') {
hasDefaultAction = true;
} else {
notification.addButton(this._makeButton(actionId, label, useActionIcons), Lang.bind(this, function() {
this._emitActionInvoked(ndata.id, actionId);
}));
}
}
}
if (hasDefaultAction) {
notification.connect('clicked', Lang.bind(this, function() {
this._emitActionInvoked(ndata.id, 'default');
}));
} else {
notification.connect('clicked', Lang.bind(this, function() {
source.open();
}));
}
switch (hints.urgency) {
case Urgency.LOW:
notification.setUrgency(MessageTray.Urgency.LOW);
@ -555,7 +411,6 @@ const NotificationDaemon = new Lang.Class({
notification.setUrgency(MessageTray.Urgency.CRITICAL);
break;
}
notification.setResident(hints.resident == true);
// 'transient' is a reserved keyword in JS, so we have to retrieve the value
// of the 'transient' hint with hints['transient'] rather than hints.transient
notification.setTransient(hints['transient'] == true);
@ -583,7 +438,6 @@ const NotificationDaemon = new Lang.Class({
'body-markup',
// 'icon-multi',
'icon-static',
'persistence',
'sound',
];
},
@ -605,7 +459,7 @@ const NotificationDaemon = new Lang.Class({
for (let i = 0; i < this._sources.length; i++) {
let source = this._sources[i];
if (source.app == tracker.focus_app) {
source.destroyNonResidentNotifications();
source.destroyNotifications();
return;
}
}
@ -620,30 +474,15 @@ const NotificationDaemon = new Lang.Class({
this._dbusImpl.emit_signal('ActionInvoked',
GLib.Variant.new('(us)', [id, action]));
},
_onTrayIconAdded: function(o, icon) {
let wmClass = icon.wm_class ? icon.wm_class.toLowerCase() : '';
if (STANDARD_TRAY_ICON_IMPLEMENTATIONS[wmClass] !== undefined)
return;
let source = this._getSource(icon.title || icon.wm_class || C_("program", "Unknown"), icon.pid, null, null, icon);
},
_onTrayIconRemoved: function(o, icon) {
let source = this._lookupSource(null, icon.pid, true);
if (source)
source.destroy();
}
});
const Source = new Lang.Class({
Name: 'NotificationDaemonSource',
const FdoNotificationDaemonSource = new Lang.Class({
Name: 'FdoNotificationDaemonSource',
Extends: MessageTray.Source,
_init: function(title, pid, sender, trayIcon, appId) {
_init: function(title, pid, sender, 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);
@ -663,20 +502,14 @@ const Source = new Lang.Class({
Lang.bind(this, this._onNameVanished));
else
this._nameWatcherId = 0;
if (this.trayIcon) {
// 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) {
if (this.app && this.app.get_app_info()) {
let id = this.app.get_id().replace(/\.desktop$/,'');
return new NotificationApplicationPolicy(id);
return new MessageTray.NotificationApplicationPolicy(id);
} else {
return new NotificationGenericPolicy();
return new MessageTray.NotificationGenericPolicy();
}
},
@ -684,48 +517,23 @@ const Source = new Lang.Class({
// Destroy the notification source when its sender is removed from DBus.
// Only do so if this.app is set to avoid removing "notify-send" sources, senders
// of which аre removed from DBus immediately.
// Sender being removed from DBus would normally result in a tray icon being removed,
// so allow the code path that handles the tray icon being removed to handle that case.
if (!this.trayIcon && this.app)
if (this.app)
this.destroy();
},
processNotification: function(notification, gicon) {
if (gicon)
this._gicon = gicon;
if (!this.trayIcon)
this.iconUpdated();
this.iconUpdated();
let tracker = Shell.WindowTracker.get_default();
if (notification.resident && this.app && tracker.focus_app == this.app)
if (this.app && tracker.focus_app == this.app)
this.pushNotification(notification);
else
this.notify(notification);
},
handleSummaryClick: function(button) {
if (!this.trayIcon)
return false;
let event = Clutter.get_current_event();
// 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 (button == 1 &&
this.notifications.length > 0)
return false;
let id = global.stage.connect('deactivate', Lang.bind(this, function () {
global.stage.disconnect(id);
this.trayIcon.click(event);
}));
Main.overview.hide();
return true;
},
_getApp: function(appId) {
let app;
@ -733,16 +541,6 @@ const Source = new Lang.Class({
if (app != null)
return app;
if (this.trayIcon) {
app = Shell.AppSystem.get_default().lookup_startup_wmclass(this.trayIcon.wm_class);
if (app != null)
return app;
app = Shell.AppSystem.get_default().lookup_desktop_wmclass(this.trayIcon.wm_class);
if (app != null)
return app;
}
if (appId) {
app = Shell.AppSystem.get_default().lookup_app(appId + '.desktop');
if (app != null)
@ -752,22 +550,6 @@ const Source = new Lang.Class({
return null;
},
_setApp: function(appId) {
if (this.app)
return;
this.app = this._getApp(appId);
if (!this.app)
return;
// Only override the icon if we were previously using
// notification-based icons (ie, not a trayicon) or if it was unset before
if (!this.trayIcon) {
this.useNotificationIcon = false;
this.iconUpdated();
}
},
setTitle: function(title) {
// Do nothing if .app is set, we don't want to override the
// app name with whatever is provided through libnotify (usually
@ -778,25 +560,21 @@ const Source = new Lang.Class({
this.parent(title);
},
open: function(notification) {
this.destroyNonResidentNotifications();
open: function() {
this.openApp();
this.destroyNotifications();
},
_lastNotificationRemoved: function() {
if (!this.trayIcon)
this.destroy();
this.destroy();
},
openApp: function() {
if (this.app == null)
return;
let windows = this.app.get_windows();
if (windows.length > 0) {
let mostRecentWindow = windows[0];
Main.activateWindow(mostRecentWindow);
}
this.app.activate();
Main.overview.hide();
},
destroy: function() {
@ -809,11 +587,7 @@ const Source = new Lang.Class({
},
createIcon: function(size) {
if (this.trayIcon) {
return new Clutter.Clone({ width: size,
height: size,
source: this.trayIcon });
} else if (this.app) {
if (this.app) {
return this.app.create_icon_texture(size);
} else if (this._gicon) {
return new St.Icon({ gicon: this._gicon,
@ -823,3 +597,276 @@ const Source = new Lang.Class({
}
}
});
const GtkNotificationDaemonNotification = new Lang.Class({
Name: 'GtkNotificationDaemonNotification',
Extends: MessageTray.Notification,
_init: function(source, notification) {
this.parent(source);
this._serialized = GLib.Variant.new('a{sv}', notification);
let { "title": title,
"body": body,
"icon": gicon,
"urgent": urgent,
"buttons": buttons,
"default-action": defaultAction,
"default-action-target": defaultActionTarget } = notification;
this.setUrgency(urgent.unpack() ? MessageTray.Urgency.CRITICAL
: MessageTray.Urgency.NORMAL);
if (buttons) {
buttons.deep_unpack().forEach(Lang.bind(this, function(button) {
this.addAction(button.label.unpack(),
Lang.bind(this, this._onButtonClicked, button));
}));
}
this._defaultAction = defaultAction ? defaultAction.unpack() : null;
this._defaultActionTarget = defaultActionTarget;
this.update(title.unpack(), body ? body.unpack() : null,
{ gicon: gicon ? Gio.icon_deserialize(gicon) : null });
},
_activateAction: function(namespacedActionId, target) {
if (namespacedActionId) {
if (namespacedActionId.startsWith('app.')) {
let actionId = namespacedActionId.slice('app.'.length);
this.source.activateAction(actionId, target);
}
} else {
this.source.open();
}
},
_onButtonClicked: function(button) {
let { 'action': action, 'target': actionTarget } = button;
this._activateAction(action.unpack(), actionTarget);
},
_onClicked: function() {
this._activateAction(this._defaultAction, this._defaultActionTarget);
this.parent();
},
serialize: function() {
return this._serialized;
},
});
const FdoApplicationIface = '<node> \
<interface name="org.freedesktop.Application"> \
<method name="ActivateAction"> \
<arg type="s" direction="in" /> \
<arg type="av" direction="in" /> \
<arg type="a{sv}" direction="in" /> \
</method> \
<method name="Activate"> \
<arg type="a{sv}" direction="in" /> \
</method> \
</interface> \
</node>';
const FdoApplicationProxy = Gio.DBusProxy.makeProxyWrapper(FdoApplicationIface);
function objectPathFromAppId(appId) {
return '/' + appId.replace(/\./g, '/');
}
function getPlatformData() {
let startupId = GLib.Variant.new('s', '_TIME' + global.get_current_time());
return { "desktop-startup-id": startupId };
}
function InvalidAppError() {}
const GtkNotificationDaemonAppSource = new Lang.Class({
Name: 'GtkNotificationDaemonAppSource',
Extends: MessageTray.Source,
_init: function(appId) {
this._appId = appId;
this._objectPath = objectPathFromAppId(appId);
this._app = Shell.AppSystem.get_default().lookup_app(appId + '.desktop');
if (!this._app)
throw new InvalidAppError();
this._notifications = {};
this.parent(this._app.get_name());
},
createIcon: function(size) {
return this._app.create_icon_texture(size);
},
_createPolicy: function() {
return new MessageTray.NotificationApplicationPolicy(this._appId);
},
_createApp: function() {
return new FdoApplicationProxy(Gio.DBus.session, this._appId, this._objectPath);
},
activateAction: function(actionId, target) {
let app = this._createApp();
app.ActivateActionRemote(actionId, target ? [target] : [], getPlatformData());
},
open: function() {
let app = this._createApp();
app.ActivateRemote(getPlatformData());
},
addNotification: function(notificationId, notificationParams, showBanner) {
if (this._notifications[notificationId])
this._notifications[notificationId].destroy();
let notification = new GtkNotificationDaemonNotification(this, notificationParams);
notification.connect('destroy', Lang.bind(this, function() {
delete this._notifications[notificationId];
}));
this._notifications[notificationId] = notification;
if (showBanner)
this.notify(notification);
else
this.pushNotification(notification);
},
removeNotification: function(notificationId) {
if (this._notifications[notificationId])
this._notifications[notificationId].destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED);
},
serialize: function() {
let notifications = [];
for (let notificationId in this._notifications) {
let notification = this._notifications[notificationId];
notifications.push([notificationId, notification.serialize()]);
}
return [this._appId, notifications];
},
});
const GtkNotificationsIface = '<node> \
<interface name="org.gtk.Notifications"> \
<method name="AddNotification"> \
<arg type="s" direction="in" /> \
<arg type="s" direction="in" /> \
<arg type="a{sv}" direction="in" /> \
</method> \
<method name="RemoveNotification"> \
<arg type="s" direction="in" /> \
<arg type="s" direction="in" /> \
</method> \
</interface> \
</node>';
const GtkNotificationDaemon = new Lang.Class({
Name: 'GtkNotificationDaemon',
_init: function() {
this._sources = {};
this._loadNotifications();
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GtkNotificationsIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gtk/Notifications');
Gio.DBus.session.own_name('org.gtk.Notifications', Gio.BusNameOwnerFlags.REPLACE, null, null);
},
_ensureAppSource: function(appId) {
if (this._sources[appId])
return this._sources[appId];
let source = new GtkNotificationDaemonAppSource(appId);
source.connect('destroy', Lang.bind(this, function() {
delete this._sources[appId];
this._saveNotifications();
}));
source.connect('count-updated', Lang.bind(this, this._saveNotifications));
Main.messageTray.add(source);
this._sources[appId] = source;
return source;
},
_loadNotifications: function() {
this._isLoading = true;
let value = global.get_persistent_state('a(sa(sv))', 'notifications');
if (value) {
let sources = value.deep_unpack();
sources.forEach(Lang.bind(this, function([appId, notifications]) {
if (notifications.length == 0)
return;
let source;
try {
source = this._ensureAppSource(appId);
} catch(e if e instanceof InvalidAppError) {
return;
}
notifications.forEach(function([notificationId, notification]) {
source.addNotification(notificationId, notification.deep_unpack(), false);
});
}));
}
this._isLoading = false;
},
_saveNotifications: function() {
if (this._isLoading)
return;
let sources = [];
for (let appId in this._sources) {
let source = this._sources[appId];
sources.push(source.serialize());
}
global.set_persistent_state('notifications', new GLib.Variant('a(sa(sv))', sources));
},
AddNotificationAsync: function(params, invocation) {
let [appId, notificationId, notification] = params;
let source;
try {
source = this._ensureAppSource(appId);
} catch(e if e instanceof InvalidAppError) {
invocation.return_dbus_error('org.gtk.Notifications.InvalidApp', 'The app by ID "%s" could not be found'.format(appId));
return;
}
source.addNotification(notificationId, notification, true);
invocation.return_value(null);
},
RemoveNotificationAsync: function(params, invocation) {
let [appId, notificationId] = params;
let source = this._sources[appId];
if (source)
source.removeNotification(notificationId);
invocation.return_value(null);
},
});
const NotificationDaemon = new Lang.Class({
Name: 'NotificationDaemon',
_init: function() {
this._fdoNotificationDaemon = new FdoNotificationDaemon();
this._gtkNotificationDaemon = new GtkNotificationDaemon();
},
});

View File

@ -1,6 +1,7 @@
// -*- 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 Lang = imports.lang;
@ -65,19 +66,24 @@ const LevelBar = new Lang.Class({
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();
cr.$dispose();
}
});
const OsdWindow = new Lang.Class({
Name: 'OsdWindow',
_init: function() {
_init: function(monitorIndex) {
this._popupSize = 0;
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._monitorIndex = monitorIndex;
let constraint = new Layout.MonitorConstraint({ index: monitorIndex });
this.actor.add_constraint(constraint);
this._box = new St.BoxLayout({ style_class: 'osd-window',
vertical: true });
this.actor.add_actor(this._box);
@ -106,7 +112,6 @@ const OsdWindow = new Lang.Class({
Main.layoutManager.connect('monitors-changed',
Lang.bind(this, this._monitorsChanged));
this._monitorsChanged();
Main.uiGroup.add_child(this.actor);
},
@ -122,13 +127,15 @@ const OsdWindow = new Lang.Class({
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;
if (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() {
@ -151,6 +158,7 @@ const OsdWindow = new Lang.Class({
Mainloop.source_remove(this._hideTimeoutId);
this._hideTimeoutId = Mainloop.timeout_add(HIDE_TIMEOUT,
Lang.bind(this, this._hide));
GLib.Source.set_name_by_id(this._hideTimeoutId, '[gnome-shell] this._hide');
},
cancel: function() {
@ -172,6 +180,7 @@ const OsdWindow = new Lang.Class({
Meta.enable_unredirect_for_screen(global.screen);
})
});
return GLib.SOURCE_REMOVE;
},
_reset: function() {
@ -182,14 +191,18 @@ const OsdWindow = new Lang.Class({
_monitorsChanged: function() {
/* assume 110x110 on a 640x480 display and scale from there */
let monitor = Main.layoutManager.primaryMonitor;
let monitor = Main.layoutManager.monitors[this._monitorIndex];
if (!monitor)
return; // we are about to be removed
let scalew = monitor.width / 640.0;
let scaleh = monitor.height / 480.0;
let scale = Math.min(scalew, scaleh);
this._popupSize = 110 * Math.max(1, scale);
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
this._icon.icon_size = this._popupSize / (2 * scaleFactor);
this._box.translation_y = monitor.height / 4;
this._icon.icon_size = this._popupSize / 2;
this._box.style_changed();
},
@ -205,6 +218,60 @@ const OsdWindow = new Lang.Class({
let minWidth = this._popupSize - verticalPadding - leftBorder - rightBorder;
let minHeight = this._popupSize - horizontalPadding - topBorder - bottomBorder;
this._box.style = 'min-height: %dpx;'.format(Math.max(minWidth, minHeight));
// minWidth/minHeight here are in real pixels,
// but the theme takes measures in unscaled dimensions
let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
this._box.style = 'min-height: %dpx;'.format(Math.max(minWidth, minHeight) / scaleFactor);
}
});
const OsdWindowManager = new Lang.Class({
Name: 'OsdWindowManager',
_init: function() {
this._osdWindows = [];
Main.layoutManager.connect('monitors-changed',
Lang.bind(this, this._monitorsChanged));
this._monitorsChanged();
},
_monitorsChanged: function() {
for (let i = 0; i < Main.layoutManager.monitors.length; i++) {
if (this._osdWindows[i] == undefined)
this._osdWindows[i] = new OsdWindow(i);
}
for (let i = Main.layoutManager.monitors.length; i < this._osdWindows.length; i++) {
this._osdWindows[i].actor.destroy();
this._osdWindows[i] = null;
}
this._osdWindows.length = Main.layoutManager.monitors.length;
},
_showOsdWindow: function(monitorIndex, icon, label, level) {
this._osdWindows[monitorIndex].setIcon(icon);
this._osdWindows[monitorIndex].setLabel(label);
this._osdWindows[monitorIndex].setLevel(level);
this._osdWindows[monitorIndex].show();
},
show: function(monitorIndex, icon, label, level) {
if (monitorIndex != -1) {
for (let i = 0; i < this._osdWindows.length; i++) {
if (i == monitorIndex)
this._showOsdWindow(i, icon, label, level);
else
this._osdWindows[i].cancel();
}
} else {
for (let i = 0; i < this._osdWindows.length; i++)
this._showOsdWindow(i, icon, label, level);
}
},
hideAll: function() {
for (let i = 0; i < this._osdWindows.length; i++)
this._osdWindows[i].cancel();
}
});

View File

@ -1,6 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta;
const Mainloop = imports.mainloop;
@ -13,6 +14,7 @@ const Gdk = imports.gi.Gdk;
const Background = imports.ui.background;
const DND = imports.ui.dnd;
const LayoutManager = imports.ui.layout;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const OverviewControls = imports.ui.overviewControls;
@ -78,10 +80,8 @@ const ShellInfo = new Lang.Class({
}
this._undoCallback = undoCallback;
if (undoCallback) {
notification.addButton('system-undo', _("Undo"));
notification.connect('action-invoked', Lang.bind(this, this._onUndoClicked));
}
if (undoCallback)
notification.addAction(_("Undo"), Lang.bind(this, this._onUndoClicked));
this._source.notify(notification);
}
@ -114,9 +114,6 @@ const Overview = new Lang.Class({
// rendering options without duplicating the texture data.
let monitor = Main.layoutManager.primaryMonitor;
this._desktopFade = new St.Bin();
Main.layoutManager.overviewGroup.add_child(this._desktopFade);
let layout = new Clutter.BinLayout();
this._stack = new Clutter.Actor({ layout_manager: layout });
this._stack.add_constraint(new LayoutManager.MonitorConstraint({ primary: true }));
@ -135,6 +132,9 @@ const Overview = new Lang.Class({
Main.layoutManager.overviewGroup.add_child(this._backgroundGroup);
this._bgManagers = [];
this._desktopFade = new St.Widget();
Main.layoutManager.overviewGroup.add_child(this._desktopFade);
this._activationTime = 0;
this.visible = false; // animating to overview, in overview, animating out
@ -148,8 +148,8 @@ const Overview = new Lang.Class({
// Dash elements, or mouseover handlers in the workspaces.
this._coverPane = new Clutter.Actor({ opacity: 0,
reactive: true });
this._stack.add_actor(this._coverPane);
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
Main.layoutManager.overviewGroup.add_child(this._coverPane);
this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return Clutter.EVENT_STOP; }));
this._stack.add_actor(this._overview);
Main.layoutManager.overviewGroup.add_child(this._stack);
@ -197,11 +197,7 @@ const Overview = new Lang.Class({
Tweener.addTween(background,
{ brightness: 1.0,
time: SHADE_ANIMATION_TIME,
transition: 'easeOutQuad'
});
Tweener.addTween(background,
{ vignetteSharpness: 0.0,
vignetteSharpness: 0.0,
time: SHADE_ANIMATION_TIME,
transition: 'easeOutQuad'
});
@ -214,12 +210,8 @@ const Overview = new Lang.Class({
let background = backgrounds[i]._delegate;
Tweener.addTween(background,
{ brightness: 0.8,
time: SHADE_ANIMATION_TIME,
transition: 'easeOutQuad'
});
Tweener.addTween(background,
{ vignetteSharpness: 0.7,
{ brightness: Lightbox.VIGNETTE_BRIGHTNESS,
vignetteSharpness: Lightbox.VIGNETTE_SHARPNESS,
time: SHADE_ANIMATION_TIME,
transition: 'easeOutQuad'
});
@ -271,8 +263,6 @@ const Overview = new Lang.Class({
this._overview.add(this._controls.actor, { y_fill: true, expand: true });
this._controls.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
this._stack.add_actor(this._controls.indicatorActor);
// TODO - recalculate everything when desktop size changes
this.dashIconSize = this._dash.iconSize;
this._dash.connect('icon-size-changed',
@ -365,12 +355,15 @@ const Overview = new Lang.Class({
this._lastHoveredWindow = dragEvent.targetActor._delegate.metaWindow;
this._windowSwitchTimeoutId = Mainloop.timeout_add(DND_WINDOW_SWITCH_TIMEOUT,
Lang.bind(this, function() {
this._windowSwitchTimeoutId = 0;
this._needsFakePointerEvent = true;
Main.activateWindow(dragEvent.targetActor._delegate.metaWindow,
this._windowSwitchTimestamp);
this.hide();
this._lastHoveredWindow = null;
return GLib.SOURCE_REMOVE;
}));
GLib.Source.set_name_by_id(this._windowSwitchTimeoutId, '[gnome-shell] Main.activateWindow');
}
return DND.DragMotionResult.CONTINUE;
@ -378,6 +371,7 @@ const Overview = new Lang.Class({
_onScrollEvent: function(actor, event) {
this.emit('scroll-event', event);
return Clutter.EVENT_PROPAGATE;
},
addAction: function(action) {
@ -445,17 +439,17 @@ const Overview = new Lang.Class({
this._inDrag = false;
},
beginWindowDrag: function(source) {
this.emit('window-drag-begin');
beginWindowDrag: function(clone) {
this.emit('window-drag-begin', clone);
this._inDrag = true;
},
cancelledWindowDrag: function(source) {
this.emit('window-drag-cancelled');
cancelledWindowDrag: function(clone) {
this.emit('window-drag-cancelled', clone);
},
endWindowDrag: function(source) {
this.emit('window-drag-end');
endWindowDrag: function(clone) {
this.emit('window-drag-end', clone);
this._inDrag = false;
},
@ -491,8 +485,13 @@ const Overview = new Lang.Class({
},
fadeOutDesktop: function() {
if (!this._desktopFade.child)
this._desktopFade.child = this._getDesktopClone();
if (!this._desktopFade.get_n_children()) {
let clone = this._getDesktopClone();
if (!clone)
return;
this._desktopFade.add_child(clone);
}
this._desktopFade.opacity = 255;
this._desktopFade.show();

View File

@ -36,6 +36,7 @@ const SlideLayout = new Lang.Class({
_init: function(params) {
this._slideX = 1;
this._translationX = 0;
this._direction = SlideDirection.LEFT;
this.parent(params);
@ -55,18 +56,21 @@ const SlideLayout = new Lang.Class({
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 [, natWidth] = child.get_preferred_width(availHeight);
// Align the actor inside the clipped box, as the actor's alignment
// flags only determine what to do if the allocated box is bigger
// than the actor's box.
let realDirection = getRtlSlideDirection(this._direction, child);
let translationX = (realDirection == SlideDirection.LEFT) ?
(availWidth - natWidth) : (natWidth - availWidth);
let alignX = (realDirection == SlideDirection.LEFT) ? (availWidth - natWidth) : 0;
let actorBox = new Clutter.ActorBox({ x1: translationX,
y1: 0,
x2: child.x_expand ? availWidth : natWidth,
y2: child.y_expand ? availHeight : natHeight });
let actorBox = new Clutter.ActorBox();
actorBox.x1 = box.x1 + alignX + this._translationX;
actorBox.x2 = actorBox.x1 + (child.x_expand ? availWidth : natWidth);
actorBox.y1 = box.y1;
actorBox.y2 = actorBox.y1 + availHeight;
child.allocate(actorBox, flags);
},
@ -87,7 +91,16 @@ const SlideLayout = new Lang.Class({
get slideDirection() {
return this._direction;
}
},
set translationX(value) {
this._translationX = value;
this.layout_changed();
},
get translationX() {
return this._translationX;
},
});
const SlidingControl = new Lang.Class({
@ -96,8 +109,8 @@ const SlidingControl = new Lang.Class({
_init: function(params) {
params = Params.parse(params, { slideDirection: SlideDirection.LEFT });
this.visible = true;
this.inDrag = false;
this._visible = true;
this._inDrag = false;
this.layout = new SlideLayout();
this.layout.slideDirection = params.slideDirection;
@ -106,6 +119,7 @@ const SlidingControl = new Lang.Class({
clip_to_allocation: true });
Main.overview.connect('showing', Lang.bind(this, this._onOverviewShowing));
Main.overview.connect('hiding', Lang.bind(this, this._onOverviewHiding));
Main.overview.connect('item-drag-begin', Lang.bind(this, this._onDragBegin));
Main.overview.connect('item-drag-end', Lang.bind(this, this._onDragEnd));
@ -116,12 +130,12 @@ const SlidingControl = new Lang.Class({
Main.overview.connect('window-drag-end', Lang.bind(this, this._onWindowDragEnd));
},
getSlide: function() {
_getSlide: function() {
throw new Error('getSlide() must be overridden');
},
updateSlide: function() {
Tweener.addTween(this.layout, { slideX: this.getSlide(),
_updateSlide: function() {
Tweener.addTween(this.layout, { slideX: this._getSlide(),
time: SIDE_CONTROLS_ANIMATION_TIME,
transition: 'easeOutQuad' });
},
@ -148,28 +162,30 @@ const SlidingControl = new Lang.Class({
let translationEnd = 0;
let translation = this._getTranslation();
if (this.visible) {
if (this._visible) {
translationStart = translation;
} else {
translationEnd = translation;
}
if (this.actor.translation_x == translationEnd)
if (this.layout.translationX == translationEnd)
return;
this.actor.translation_x = translationStart;
Tweener.addTween(this.actor, { translation_x: translationEnd,
time: SIDE_CONTROLS_ANIMATION_TIME,
transition: 'easeOutQuad'
});
this.layout.translationX = translationStart;
Tweener.addTween(this.layout, { translationX: 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;
this._visible = true;
this.layout.slideX = this._getSlide();
this.layout.translationX = this._getTranslation();
this.slideIn();
},
_onOverviewHiding: function() {
this.slideOut();
},
_onWindowDragBegin: function() {
@ -181,14 +197,14 @@ const SlidingControl = new Lang.Class({
},
_onDragBegin: function() {
this.inDrag = true;
this.actor.translation_x = 0;
this.updateSlide();
this._inDrag = true;
this.layout.translationX = 0;
this._updateSlide();
},
_onDragEnd: function() {
this.inDrag = false;
this.updateSlide();
this._inDrag = false;
this._updateSlide();
},
fadeIn: function() {
@ -206,13 +222,13 @@ const SlidingControl = new Lang.Class({
},
slideIn: function() {
this.visible = true;
this._visible = true;
this._updateTranslation();
// we will update slideX and the translation from pageEmpty
},
slideOut: function() {
this.visible = false;
this._visible = false;
this._updateTranslation();
// we will update slideX from pageEmpty
},
@ -222,7 +238,7 @@ const SlidingControl = new Lang.Class({
// 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.layout.slideX = this._getSlide();
this._updateTranslation();
}
});
@ -236,26 +252,25 @@ const ThumbnailsSlider = new Lang.Class({
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));
Main.overview.connect('hiding', Lang.bind(this, this.slideOut));
this.actor.connect('notify::hover', Lang.bind(this, this.updateSlide));
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateSlide));
this.actor.connect('notify::hover', Lang.bind(this, this._updateSlide));
global.window_manager.connect('switch-workspace', Lang.bind(this, this._updateSlide));
this._thumbnailsBox.actor.bind_property('visible', this.actor, 'visible', GObject.BindingFlags.SYNC_CREATE);
},
_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;
// actually used, e.g. there are windows on any non-active workspace
let alwaysZoomOut = this.actor.hover ||
this._inDrag ||
!Meta.prefs_get_dynamic_workspaces() ||
global.screen.n_workspaces > 2 ||
global.screen.get_active_workspace_index() != 0;
if (!alwaysZoomOut) {
let monitors = Main.layoutManager.monitors;
@ -275,20 +290,13 @@ const ThumbnailsSlider = new Lang.Class({
return alwaysZoomOut;
},
_onOverviewShowing: function() {
this.visible = true;
this.layout.slideX = this.getSlide();
this.actor.translation_x = this._getTranslation();
this.slideIn();
},
getNonExpandedWidth: function() {
let child = this.actor.get_first_child();
return child.get_theme_node().get_length('visible-width');
},
getSlide: function() {
if (!this.visible)
_getSlide: function() {
if (!this._visible)
return 0;
let alwaysZoomOut = this._getAlwaysZoomOut();
@ -324,31 +332,23 @@ const DashSlider = new Lang.Class({
// 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.x_expand = true;
this.actor.x_align = Clutter.ActorAlign.START;
this.actor.y_expand = true;
this.actor.add_actor(this._dash.actor);
this._dash.connect('icon-size-changed', Lang.bind(this, this.updateSlide));
Main.overview.connect('hiding', Lang.bind(this, this.slideOut));
this._dash.connect('icon-size-changed', Lang.bind(this, this._updateSlide));
},
getSlide: function() {
if (this.visible || this.inDrag)
_getSlide: function() {
if (this._visible || this._inDrag)
return 1;
else
return 0;
},
_onOverviewShowing: function() {
this.visible = true;
this.layout.slideX = this.getSlide();
this.actor.translation_x = this._getTranslation();
this.slideIn();
},
_onWindowDragBegin: function() {
this.fadeHalf();
},
@ -396,114 +396,6 @@ const DashSpacer = new Lang.Class({
}
});
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;
let hasChats = false;
this._sources.forEach(Lang.bind(this,
function(source) {
count += source.indicatorCount;
hasChats |= source.isChat;
}));
this._count = count;
this._label.text = ngettext("%d new message",
"%d new messages",
count).format(count);
this._icon.visible = hasChats;
this._updateVisibility();
},
_updateVisibility: function() {
let activePage = this._viewSelector.getActivePage();
let visible = ((this._count > 0) && (activePage == ViewSelector.ViewPage.WINDOWS));
this.actor.visible = visible;
}
});
const ControlsLayout = new Lang.Class({
Name: 'ControlsLayout',
Extends: Clutter.BinLayout,
@ -532,9 +424,6 @@ const ControlsManager = new Lang.Class({
this.viewSelector.connect('page-changed', Lang.bind(this, this._setVisibility));
this.viewSelector.connect('page-empty', Lang.bind(this, this._onPageEmpty));
this._indicator = new MessagesIndicator(this.viewSelector);
this.indicatorActor = this._indicator.actor;
let layout = new ControlsLayout();
this.actor = new St.Widget({ layout_manager: layout,
reactive: true,

View File

@ -213,9 +213,7 @@ const AppMenuButton = new Lang.Class({
this._label = new TextShadower();
this._label.actor.y_align = Clutter.ActorAlign.CENTER;
this._hbox.add_actor(this._label.actor);
this._arrow = new St.Label({ text: '\u25BE',
y_expand: true,
y_align: Clutter.ActorAlign.CENTER });
this._arrow = PopupMenu.arrowIcon(St.Side.BOTTOM);
this._hbox.add_actor(this._arrow);
this._iconBottomClip = 0;
@ -597,6 +595,7 @@ const ActivitiesButton = new Lang.Class({
Mainloop.source_remove(this._xdndTimeOut);
this._xdndTimeOut = Mainloop.timeout_add(BUTTON_DND_ACTIVATION_TIMEOUT,
Lang.bind(this, this._xdndToggleOverview, actor));
GLib.Source.set_name_by_id(this._xdndTimeOut, '[gnome-shell] this._xdndToggleOverview');
return DND.DragMotionResult.CONTINUE;
},
@ -604,14 +603,15 @@ const ActivitiesButton = new Lang.Class({
_onCapturedEvent: function(actor, event) {
if (event.type() == Clutter.EventType.BUTTON_PRESS) {
if (!Main.overview.shouldToggleByCornerOrButton())
return true;
return Clutter.EVENT_STOP;
}
return false;
return Clutter.EVENT_PROPAGATE;
},
_onButtonRelease: function() {
Main.overview.toggle();
this.menu.close();
return Clutter.EVENT_PROPAGATE;
},
_onKeyRelease: function(actor, event) {
@ -619,6 +619,7 @@ const ActivitiesButton = new Lang.Class({
if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) {
Main.overview.toggle();
}
return Clutter.EVENT_PROPAGATE;
},
_xdndToggleOverview: function(actor) {
@ -630,6 +631,7 @@ const ActivitiesButton = new Lang.Class({
Mainloop.source_remove(this._xdndTimeOut);
this._xdndTimeOut = 0;
return GLib.SOURCE_REMOVE;
}
});
@ -811,7 +813,11 @@ const AggregateMenu = new Lang.Class({
this._indicators = new St.BoxLayout({ style_class: 'panel-status-indicators-box' });
this.actor.add_child(this._indicators);
this._network = new imports.ui.status.network.NMApplet();
if (Config.HAVE_NETWORKMANAGER) {
this._network = new imports.ui.status.network.NMApplet();
} else {
this._network = null;
}
if (Config.HAVE_BLUETOOTH) {
this._bluetooth = new imports.ui.status.bluetooth.Indicator();
} else {
@ -824,26 +830,31 @@ const AggregateMenu = new Lang.Class({
this._brightness = new imports.ui.status.brightness.Indicator();
this._system = new imports.ui.status.system.Indicator();
this._screencast = new imports.ui.status.screencast.Indicator();
this._location = new imports.ui.status.location.Indicator();
this._indicators.add_child(this._screencast.indicators);
this._indicators.add_child(this._network.indicators);
this._indicators.add_child(this._location.indicators);
if (this._network) {
this._indicators.add_child(this._network.indicators);
}
if (this._bluetooth) {
this._indicators.add_child(this._bluetooth.indicators);
}
this._indicators.add_child(this._rfkill.indicators);
this._indicators.add_child(this._volume.indicators);
this._indicators.add_child(this._power.indicators);
this._indicators.add_child(new St.Label({ text: '\u25BE',
y_expand: true,
y_align: Clutter.ActorAlign.CENTER }));
this._indicators.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
this.menu.addMenuItem(this._volume.menu);
this.menu.addMenuItem(this._brightness.menu);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addMenuItem(this._network.menu);
if (this._network) {
this.menu.addMenuItem(this._network.menu);
}
if (this._bluetooth) {
this.menu.addMenuItem(this._bluetooth.menu);
}
this.menu.addMenuItem(this._location.menu);
this.menu.addMenuItem(this._rfkill.menu);
this.menu.addMenuItem(this._power.menu);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
@ -987,23 +998,23 @@ const Panel = new Lang.Class({
_onButtonPress: function(actor, event) {
if (Main.modalCount > 0)
return false;
return Clutter.EVENT_PROPAGATE;
if (event.get_source() != actor)
return false;
return Clutter.EVENT_PROPAGATE;
let button = event.get_button();
if (button != 1)
return false;
return Clutter.EVENT_PROPAGATE;
let focusWindow = global.display.focus_window;
if (!focusWindow)
return false;
return Clutter.EVENT_PROPAGATE;
let dragWindow = focusWindow.is_attached_dialog() ? focusWindow.get_transient_for()
: focusWindow;
if (!dragWindow)
return false;
return Clutter.EVENT_PROPAGATE;
let rect = dragWindow.get_outer_rect();
let [stageX, stageY] = event.get_coords();
@ -1012,7 +1023,7 @@ const Panel = new Lang.Class({
stageX > rect.x && stageX < rect.x + rect.width;
if (!allowDrag)
return false;
return Clutter.EVENT_PROPAGATE;
global.display.begin_grab_op(global.screen,
dragWindow,
@ -1024,7 +1035,7 @@ const Panel = new Lang.Class({
event.get_time(),
stageX, stageY);
return true;
return Clutter.EVENT_STOP;
},
toggleAppMenu: function() {

View File

@ -137,29 +137,30 @@ const Button = new Lang.Class({
_onButtonPress: function(actor, event) {
if (!this.menu)
return;
return Clutter.EVENT_PROPAGATE;
this.menu.toggle();
return Clutter.EVENT_PROPAGATE;
},
_onSourceKeyPress: function(actor, event) {
if (!this.menu)
return false;
return Clutter.EVENT_PROPAGATE;
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
this.menu.toggle();
return true;
return Clutter.EVENT_STOP;
} else if (symbol == Clutter.KEY_Escape && this.menu.isOpen) {
this.menu.close();
return true;
return Clutter.EVENT_STOP;
} else if (symbol == Clutter.KEY_Down) {
if (!this.menu.isOpen)
this.menu.toggle();
this.menu.actor.navigate_focus(this.actor, Gtk.DirectionType.DOWN, false);
return true;
return Clutter.EVENT_STOP;
} else
return false;
return Clutter.EVENT_PROPAGATE;
},
_onVisibilityChanged: function() {
@ -172,7 +173,7 @@ const Button = new Lang.Class({
_onMenuKeyPress: function(actor, event) {
if (global.focus_manager.navigate_from_event(event))
return true;
return Clutter.EVENT_STOP;
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) {
@ -180,10 +181,10 @@ const Button = new Lang.Class({
if (group) {
let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT;
group.navigate_focus(this.actor, direction, false);
return true;
return Clutter.EVENT_STOP;
}
}
return false;
return Clutter.EVENT_PROPAGATE;
},
_onOpenStateChanged: function(menu, open) {

View File

@ -1,5 +1,6 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@ -106,11 +107,12 @@ const PointerWatcher = new Lang.Class({
this._timeoutId = Mainloop.timeout_add(minInterval,
Lang.bind(this, this._onTimeout));
GLib.Source.set_name_by_id(this._timeoutId, '[gnome-shell] this._onTimeout');
},
_onTimeout: function() {
this._updatePointer();
return true;
return GLib.SOURCE_CONTINUE;
},
_updatePointer: function() {

View File

@ -42,6 +42,40 @@ function isPopupMenuItemVisible(child) {
return child.visible;
}
/**
* @side Side to which the arrow points.
*/
function arrowIcon(side) {
let rotation;
switch (side) {
case St.Side.TOP:
rotation = 180;
break;
case St.Side.RIGHT:
rotation = - 90;
break;
case St.Side.BOTTOM:
rotation = 0;
break;
case St.Side.LEFT:
rotation = 90;
break;
}
let gicon = new Gio.FileIcon({ file: Gio.File.new_for_path(global.datadir +
'/theme/menu-arrow-symbolic.svg') });
let arrow = new St.Icon({ style_class: 'popup-menu-arrow',
gicon: gicon,
accessible_role: Atk.Role.ARROW,
y_expand: true,
y_align: Clutter.ActorAlign.CENTER });
arrow.rotation_angle_z = rotation;
return arrow;
}
const PopupBaseMenuItem = new Lang.Class({
Name: 'PopupBaseMenuItem',
@ -84,6 +118,7 @@ const PopupBaseMenuItem = new Lang.Class({
this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
this.actor.connect('key-focus-out', Lang.bind(this, this._onKeyFocusOut));
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
},
_getTopMenu: function() {
@ -99,7 +134,7 @@ const PopupBaseMenuItem = new Lang.Class({
_onButtonReleaseEvent: function (actor, event) {
this.activate(event);
return true;
return Clutter.EVENT_STOP;
},
_onKeyPressEvent: function (actor, event) {
@ -107,9 +142,9 @@ const PopupBaseMenuItem = new Lang.Class({
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
this.activate(event);
return true;
return Clutter.EVENT_STOP;
}
return false;
return Clutter.EVENT_PROPAGATE;
},
_onKeyFocusIn: function (actor) {
@ -165,6 +200,9 @@ const PopupBaseMenuItem = new Lang.Class({
destroy: function() {
this.actor.destroy();
},
_onDestroy: function() {
this.emit('destroy');
},
@ -213,8 +251,16 @@ const PopupSeparatorMenuItem = new Lang.Class({
this.actor.add(this.label);
this.actor.label_actor = this.label;
this.label.connect('notify::text',
Lang.bind(this, this._syncVisibility));
this._syncVisibility();
this._separator = new Separator.HorizontalSeparator({ style_class: 'popup-separator-menu-item' });
this.actor.add(this._separator.actor, { expand: true });
},
_syncVisibility: function() {
this.label.visible = this.label.text != '';
}
});
@ -841,14 +887,14 @@ const PopupSubMenu = new Lang.Class({
if (animate) {
let [minHeight, naturalHeight] = this.actor.get_preferred_height(-1);
this.actor.height = 0;
this.actor._arrow_rotation = this._arrow.rotation_angle_z;
this.actor._arrowRotation = this._arrow.rotation_angle_z;
Tweener.addTween(this.actor,
{ _arrow_rotation: 90,
{ _arrowRotation: this.actor._arrowRotation + 90,
height: naturalHeight,
time: 0.25,
onUpdateScope: this,
onUpdate: function() {
this._arrow.rotation_angle_z = this.actor._arrow_rotation;
this._arrow.rotation_angle_z = this.actor._arrowRotation;
},
onCompleteScope: this,
onComplete: function() {
@ -856,7 +902,7 @@ const PopupSubMenu = new Lang.Class({
}
});
} else {
this._arrow.rotation_angle_z = 90;
this._arrow.rotation_angle_z = this.actor._arrowRotation + 90;
}
},
@ -874,14 +920,14 @@ const PopupSubMenu = new Lang.Class({
animate = false;
if (animate) {
this.actor._arrow_rotation = this._arrow.rotation_angle_z;
this.actor._arrowRotation = this._arrow.rotation_angle_z;
Tweener.addTween(this.actor,
{ _arrow_rotation: 0,
{ _arrowRotation: this.actor._arrowRotation - 90,
height: 0,
time: 0.25,
onUpdateScope: this,
onUpdate: function() {
this._arrow.rotation_angle_z = this.actor._arrow_rotation;
this._arrow.rotation_angle_z = this.actor._arrowRotation;
},
onCompleteScope: this,
onComplete: function() {
@ -890,7 +936,7 @@ const PopupSubMenu = new Lang.Class({
},
});
} else {
this._arrow.rotation_angle_z = 0;
this._arrow.rotation_angle_z = this.actor._arrowRotation - 90;
this.actor.hide();
}
},
@ -901,10 +947,10 @@ const PopupSubMenu = new Lang.Class({
if (this.isOpen && event.get_key_symbol() == Clutter.KEY_Left) {
this.close(BoxPointer.PopupAnimation.FULL);
this.sourceActor._delegate.setActive(true);
return true;
return Clutter.EVENT_STOP;
}
return false;
return Clutter.EVENT_PROPAGATE;
}
});
@ -962,8 +1008,7 @@ const PopupSubMenuMenuItem = new Lang.Class({
y_align: Clutter.ActorAlign.CENTER });
this.actor.add_child(this.status);
this._triangle = new St.Label({ text: '\u25B8',
style_class: 'popup-submenu-menu-item-triangle' });
this._triangle = arrowIcon(St.Side.RIGHT);
this._triangle.pivot_point = new Clutter.Point({ x: 0.5, y: 0.6 });
this._triangleBin = new St.Widget({ y_expand: true,
@ -1030,10 +1075,10 @@ const PopupSubMenuMenuItem = new Lang.Class({
if (symbol == Clutter.KEY_Right) {
this._setOpenState(true);
this.menu.actor.navigate_focus(null, Gtk.DirectionType.DOWN, false);
return true;
return Clutter.EVENT_STOP;
} else if (symbol == Clutter.KEY_Left && this._getOpenState()) {
this._setOpenState(false);
return true;
return Clutter.EVENT_STOP;
}
return this.parent(actor, event);
@ -1045,6 +1090,7 @@ const PopupSubMenuMenuItem = new Lang.Class({
_onButtonReleaseEvent: function(actor) {
this._setOpenState(!this._getOpenState());
return Clutter.EVENT_PROPAGATE;
}
});
@ -1076,7 +1122,7 @@ const PopupMenuManager = new Lang.Class({
if (source) {
if (!menu.blockSourceEvents)
this._grabHelper.addActor(source);
menudata.enterId = source.connect('enter-event', Lang.bind(this, function() { this._onMenuSourceEnter(menu); }));
menudata.enterId = source.connect('enter-event', Lang.bind(this, function() { return this._onMenuSourceEnter(menu); }));
menudata.focusInId = source.connect('key-focus-in', Lang.bind(this, function() { this._onMenuSourceEnter(menu); }));
}
@ -1088,7 +1134,7 @@ const PopupMenuManager = new Lang.Class({
removeMenu: function(menu) {
if (menu == this.activeMenu)
this._closeMenu(menu);
this._closeMenu(false, menu);
let position = this._findMenu(menu);
if (position == -1) // not a menu we manage
@ -1138,13 +1184,13 @@ const PopupMenuManager = new Lang.Class({
_onMenuSourceEnter: function(menu) {
if (!this._grabHelper.grabbed)
return false;
return Clutter.EVENT_PROPAGATE;
if (this._grabHelper.isActorGrabbed(menu.actor))
return false;
return Clutter.EVENT_PROPAGATE;
this._changeMenu(menu);
return false;
return Clutter.EVENT_PROPAGATE;
},
_onMenuDestroy: function(menu) {

View File

@ -7,58 +7,63 @@ const Lang = imports.lang;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const FileUtils = imports.misc.fileUtils;
const Search = imports.ui.search;
const KEY_FILE_GROUP = 'Shell Search Provider';
const SearchProviderIface = <interface name="org.gnome.Shell.SearchProvider">
<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" />
</method>
</interface>;
const SearchProviderIface = '<node> \
<interface name="org.gnome.Shell.SearchProvider"> \
<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" /> \
</method> \
</interface> \
</node>';
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>;
const SearchProvider2Iface = '<node> \
<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> \
</node>';
var SearchProviderProxy = Gio.DBusProxy.makeProxyWrapper(SearchProviderIface);
var SearchProvider2Proxy = Gio.DBusProxy.makeProxyWrapper(SearchProvider2Iface);
var SearchProviderProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProviderIface);
var SearchProvider2ProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProvider2Iface);
function loadRemoteSearchProviders(addProviderCallback) {
function loadRemoteSearchProviders(callback) {
let objectPaths = {};
let loadedProviders = [];
@ -112,30 +117,25 @@ function loadRemoteSearchProviders(addProviderCallback) {
}
}
let dataDirs = GLib.get_system_data_dirs();
dataDirs.forEach(function(dataDir) {
let path = GLib.build_filenamev([dataDir, 'gnome-shell', 'search-providers']);
let dir = Gio.File.new_for_path(path);
let fileEnum;
try {
fileEnum = dir.enumerate_children('standard::name,standard::type',
Gio.FileQueryInfoFlags.NONE, null);
} catch (e) {
fileEnum = null;
}
if (fileEnum != null) {
let info;
while ((info = fileEnum.next_file(null)))
loadRemoteSearchProvider(fileEnum.get_child(info));
}
});
let searchSettings = new Gio.Settings({ schema: Search.SEARCH_PROVIDERS_SCHEMA });
if (searchSettings.get_boolean('disable-external')) {
callback([]);
return;
}
FileUtils.collectFromDatadirs('search-providers', false, loadRemoteSearchProvider);
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');
loadedProviders = loadedProviders.filter(function(provider) {
let appId = provider.appInfo.get_id();
let disabled = searchSettings.get_strv('disabled');
return disabled.indexOf(appId) == -1;
});
loadedProviders.sort(function(providerA, providerB) {
let idxA, idxB;
let appIdA, appIdB;
@ -166,32 +166,34 @@ function loadRemoteSearchProviders(addProviderCallback) {
return (idxA - idxB);
});
loadedProviders.forEach(addProviderCallback);
callback(loadedProviders);
}
const RemoteSearchProvider = new Lang.Class({
Name: 'RemoteSearchProvider',
_init: function(appInfo, dbusName, dbusPath, proxyType) {
if (!proxyType)
proxyType = SearchProviderProxy;
_init: function(appInfo, dbusName, dbusPath, proxyInfo) {
if (!proxyInfo)
proxyInfo = SearchProviderProxyInfo;
this.proxy = new proxyType(Gio.DBus.session,
dbusName, dbusPath, Lang.bind(this, this._onProxyConstructed));
this.proxy = new Gio.DBusProxy({ g_bus_type: Gio.BusType.SESSION,
g_name: dbusName,
g_object_path: dbusPath,
g_interface_info: proxyInfo,
g_interface_name: proxyInfo.name,
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START_AT_CONSTRUCTION |
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
this.proxy.init_async(GLib.PRIORITY_DEFAULT, null, null);
this.appInfo = appInfo;
this.id = appInfo.get_id();
this.isRemoteProvider = true;
this._cancellable = new Gio.Cancellable();
},
_onProxyConstructed: function(proxy) {
// Do nothing
},
createIcon: function(size, meta) {
let gicon;
let gicon = null;
let icon = null;
if (meta['icon']) {
gicon = Gio.icon_deserialize(meta['icon']);
} else if (meta['gicon']) {
@ -203,44 +205,49 @@ const RemoteSearchProvider = new Lang.Class({
bitsPerSample, width, height, rowStride);
}
return new St.Icon({ gicon: gicon,
icon_size: size });
if (gicon)
icon = new St.Icon({ gicon: gicon,
icon_size: size });
return icon;
},
_getResultsFinished: function(results, error) {
if (error)
filterResults: function(results, maxNumber) {
if (results.length <= maxNumber)
return results;
let regularResults = results.filter(function(r) { return !r.startsWith('special:'); });
let specialResults = results.filter(function(r) { return r.startsWith('special:'); });
return regularResults.slice(0, maxNumber).concat(specialResults.slice(0, maxNumber));
},
_getResultsFinished: function(results, error, callback) {
if (error) {
if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
log('Received error from DBus search provider %s: %s'.format(this.id, String(error)));
callback([]);
return;
this.searchSystem.setResults(this, results[0]);
}
callback(results[0]);
},
getInitialResultSet: function(terms) {
this._cancellable.cancel();
this._cancellable.reset();
try {
this.proxy.GetInitialResultSetRemote(terms,
Lang.bind(this, this._getResultsFinished),
this._cancellable);
} catch(e) {
log('Error calling GetInitialResultSet for provider %s: %s'.format(this.id, e.toString()));
this.searchSystem.setResults(this, []);
}
getInitialResultSet: function(terms, callback, cancellable) {
this.proxy.GetInitialResultSetRemote(terms,
Lang.bind(this, this._getResultsFinished, callback),
cancellable);
},
getSubsearchResultSet: function(previousResults, newTerms) {
this._cancellable.cancel();
this._cancellable.reset();
try {
this.proxy.GetSubsearchResultSetRemote(previousResults, newTerms,
Lang.bind(this, this._getResultsFinished),
this._cancellable);
} catch(e) {
log('Error calling GetSubsearchResultSet for provider %s: %s'.format(this.id, e.toString()));
this.searchSystem.setResults(this, []);
}
getSubsearchResultSet: function(previousResults, newTerms, callback, cancellable) {
this.proxy.GetSubsearchResultSetRemote(previousResults, newTerms,
Lang.bind(this, this._getResultsFinished, callback),
cancellable);
},
_getResultMetasFinished: function(results, error, callback) {
if (error) {
if (!error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
log('Received error from DBus search provider %s during GetResultMetas: %s'.format(this.id, String(error)));
callback([]);
return;
}
@ -262,17 +269,10 @@ const RemoteSearchProvider = new Lang.Class({
callback(resultMetas);
},
getResultMetas: function(ids, callback) {
this._cancellable.cancel();
this._cancellable.reset();
try {
this.proxy.GetResultMetasRemote(ids,
Lang.bind(this, this._getResultMetasFinished, callback),
this._cancellable);
} catch(e) {
log('Error calling GetResultMetas for provider %s: %s'.format(this.id, e.toString()));
callback([]);
}
getResultMetas: function(ids, callback, cancellable) {
this.proxy.GetResultMetasRemote(ids,
Lang.bind(this, this._getResultMetasFinished, callback),
cancellable);
},
activateResult: function(id) {
@ -283,7 +283,7 @@ const RemoteSearchProvider = new Lang.Class({
// 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());
this.appInfo.launch([], global.create_app_launch_context(0, -1));
}
});
@ -292,7 +292,7 @@ const RemoteSearchProvider2 = new Lang.Class({
Extends: RemoteSearchProvider,
_init: function(appInfo, dbusName, dbusPath) {
this.parent(appInfo, dbusName, dbusPath, SearchProvider2Proxy);
this.parent(appInfo, dbusName, dbusPath, SearchProvider2ProxyInfo);
this.canLaunchSearch = true;
},

View File

@ -73,7 +73,9 @@ const RunDialog = new Lang.Class({
let label = new St.Label({ style_class: 'run-dialog-label',
text: _("Enter a Command") });
this.contentLayout.add(label, { y_align: St.Align.START });
this.contentLayout.add(label, { x_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
let entry = new St.Entry({ style_class: 'run-dialog-entry',
can_focus: true });
@ -101,6 +103,8 @@ const RunDialog = new Lang.Class({
this._errorMessage.clutter_text.line_wrap = true;
this._errorBox.add(this._errorMessage, { expand: true,
x_align: St.Align.START,
x_fill: false,
y_align: St.Align.MIDDLE,
y_fill: false });
@ -124,7 +128,7 @@ const RunDialog = new Lang.Class({
!this.pushModal())
this.close();
return true;
return Clutter.EVENT_STOP;
}
if (symbol == Clutter.Tab) {
let text = o.get_text();
@ -138,9 +142,9 @@ const RunDialog = new Lang.Class({
o.insert_text(postfix, -1);
o.set_cursor_position(text.length + postfix.length);
}
return true;
return Clutter.EVENT_STOP;
}
return false;
return Clutter.EVENT_PROPAGATE;
}));
},
@ -229,7 +233,7 @@ const RunDialog = new Lang.Class({
let file = Gio.file_new_for_path(path);
try {
Gio.app_info_launch_default_for_uri(file.get_uri(),
global.create_app_launch_context());
global.create_app_launch_context(0, -1));
} catch (e) {
// The exception from gjs contains an error string like:
// Error invoking Gio.app_info_launch_default_for_uri: No application

View File

@ -17,8 +17,8 @@ const TweenerEquations = imports.tweener.equations;
const Background = imports.ui.background;
const GnomeSession = imports.misc.gnomeSession;
const Hash = imports.misc.hash;
const Layout = imports.ui.layout;
const OVirt = imports.gdm.oVirt;
const LoginManager = imports.misc.loginManager;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
@ -114,7 +114,7 @@ const NotificationsBox = new Lang.Class({
this.actor.add(this._musicBin);
this.actor.add(this._scrollView, { x_fill: true, x_align: St.Align.START });
this._sources = new Hash.Map();
this._sources = new Map();
Main.messageTray.getSources().forEach(Lang.bind(this, function(source) {
this._sourceAdded(Main.messageTray, source, true);
}));
@ -129,9 +129,8 @@ const NotificationsBox = new Lang.Class({
this._sourceAddedId = 0;
}
let items = this._sources.items();
for (let i = 0; i < items.length; i++) {
let [source, obj] = items[i];
let items = this._sources.entries();
for (let [source, obj] of items) {
this._removeSource(source, obj);
}
@ -197,8 +196,8 @@ const NotificationsBox = new Lang.Class({
let body = '';
if (n.bannerBodyText) {
body = n.bannerBodyMarkup ? n.bannerBodyText :
GLib.markup_escape_text(n.bannerBodyMarkup, -1);
body = n.bannerBodyMarkup ? n.bannerBodyText
: GLib.markup_escape_text(n.bannerBodyText, -1);
}
let label = new St.Label({ style_class: 'screen-shield-notification-count-text' });
@ -239,10 +238,6 @@ const NotificationsBox = new Lang.Class({
},
_sourceAdded: function(tray, source, initial) {
// Ignore transient sources
if (source.isTransient)
return;
let obj = {
visible: source.policy.showInLockScreen,
detailed: source.policy.detailsInLockScreen,
@ -306,7 +301,7 @@ const NotificationsBox = new Lang.Class({
});
this._updateVisibility();
Shell.util_wake_up_screen();
this.emit('wake-up-screen');
}
},
@ -332,7 +327,7 @@ const NotificationsBox = new Lang.Class({
this._updateVisibility();
if (obj.sourceBox.visible)
Shell.util_wake_up_screen();
this.emit('wake-up-screen');
},
_visibleChanged: function(source, obj) {
@ -347,7 +342,7 @@ const NotificationsBox = new Lang.Class({
this._updateVisibility();
if (obj.sourceBox.visible)
Shell.util_wake_up_screen();
this.emit('wake-up-screen');
},
_detailedChanged: function(source, obj) {
@ -385,6 +380,7 @@ const NotificationsBox = new Lang.Class({
this._sources.delete(source);
},
});
Signals.addSignalMethods(NotificationsBox.prototype);
const Arrow = new Lang.Class({
Name: 'Arrow',
@ -418,6 +414,7 @@ const Arrow = new Lang.Class({
cr.lineTo(w/2, thickness);
cr.lineTo(w - thickness / 2, h - thickness / 2);
cr.stroke();
cr.$dispose();
},
vfunc_style_changed: function() {
@ -540,11 +537,18 @@ const ScreenShield = new Lang.Class({
this._smartcardManager = SmartcardManager.getSmartcardManager();
this._smartcardManager.connect('smartcard-inserted',
Lang.bind(this, function(token) {
Lang.bind(this, function(manager, token) {
if (this._isLocked && token.UsedToLogin)
this._liftShield(true, 0);
}));
this._oVirtCredentialsManager = OVirt.getOVirtCredentialsManager();
this._oVirtCredentialsManager.connect('user-authenticated',
Lang.bind(this, function() {
if (this._isLocked)
this._liftShield(true, 0);
}));
this._inhibitor = null;
this._aboutToSuspend = false;
this._loginManager = LoginManager.getLoginManager();
@ -665,11 +669,11 @@ const ScreenShield = new Lang.Class({
// down after cancel.
if (this._lockScreenState != MessageTray.State.SHOWN)
return false;
return Clutter.EVENT_PROPAGATE;
let isEnter = (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_KP_Enter);
if (!isEnter && !(GLib.unichar_isprint(unichar) || symbol == Clutter.KEY_Escape))
return false;
return Clutter.EVENT_PROPAGATE;
if (this._isLocked &&
this._ensureUnlockDialog(true, true) &&
@ -677,12 +681,12 @@ const ScreenShield = new Lang.Class({
this._dialog.addCharacter(unichar);
this._liftShield(true, 0);
return true;
return Clutter.EVENT_STOP;
},
_onLockScreenScroll: function(actor, event) {
if (this._lockScreenState != MessageTray.State.SHOWN)
return false;
return Clutter.EVENT_PROPAGATE;
let delta = 0;
if (event.get_scroll_direction() == Clutter.ScrollDirection.UP)
@ -697,7 +701,7 @@ const ScreenShield = new Lang.Class({
this._liftShield(true, 0);
}
return true;
return Clutter.EVENT_STOP;
},
_inhibitSuspend: function() {
@ -725,7 +729,7 @@ const ScreenShield = new Lang.Class({
} else {
this._inhibitSuspend();
this._onUserBecameActive();
this._wakeUpScreen();
}
},
@ -748,7 +752,7 @@ const ScreenShield = new Lang.Class({
});
}
return true;
return GLib.SOURCE_CONTINUE;
},
_onDragBegin: function() {
@ -844,8 +848,9 @@ const ScreenShield = new Lang.Class({
Lang.bind(this, function() {
this._lockTimeoutId = 0;
this.lock(false);
return false;
return GLib.SOURCE_REMOVE;
}));
GLib.Source.set_name_by_id(this._lockTimeoutId, '[gnome-shell] this.lock');
}
this._activateFade(this._longLightbox, STANDARD_FADE_TIME);
@ -1037,6 +1042,7 @@ const ScreenShield = new Lang.Class({
if (!this._arrowAnimationId) {
this._arrowAnimationId = Mainloop.timeout_add(6000, Lang.bind(this, this._animateArrows));
GLib.Source.set_name_by_id(this._arrowAnimationId, '[gnome-shell] this._animateArrows');
this._animateArrows();
}
@ -1093,7 +1099,7 @@ const ScreenShield = new Lang.Class({
global.stage.disconnect(motionId);
}
return false;
return Clutter.EVENT_PROPAGATE;
}));
this._cursorTracker.set_pointer_visible(false);
@ -1104,9 +1110,11 @@ const ScreenShield = new Lang.Class({
if (params.fadeToBlack && params.animateFade) {
// Take a beat
Mainloop.timeout_add(1000 * MANUAL_FADE_TIME, Lang.bind(this, function() {
let id = Mainloop.timeout_add(1000 * MANUAL_FADE_TIME, Lang.bind(this, function() {
this._activateFade(this._shortLightbox, MANUAL_FADE_TIME);
return GLib.SOURCE_REMOVE;
}));
GLib.Source.set_name_by_id(id, '[gnome-shell] this._activateFade');
} else {
if (params.fadeToBlack)
this._activateFade(this._shortLightbox, 0);
@ -1147,6 +1155,7 @@ const ScreenShield = new Lang.Class({
this._lockScreenContents.add_actor(this._lockScreenContentsBox);
this._notificationsBox = new NotificationsBox();
this._wakeUpScreenId = this._notificationsBox.connect('wake-up-screen', Lang.bind(this, this._wakeUpScreen));
this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true,
y_fill: true,
expand: true });
@ -1154,11 +1163,17 @@ const ScreenShield = new Lang.Class({
this._hasLockScreen = true;
},
_wakeUpScreen: function() {
this._onUserBecameActive();
this.emit('wake-up-screen');
},
_clearLockScreen: function() {
this._clock.destroy();
this._clock = null;
if (this._notificationsBox) {
this._notificationsBox.disconnect(this._wakeUpScreenId);
this._notificationsBox.destroy();
this._notificationsBox = null;
}

View File

@ -6,30 +6,31 @@ const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const Hash = imports.misc.hash;
const Main = imports.ui.main;
const ScreencastIface = <interface name="org.gnome.Shell.Screencast">
<method name="Screencast">
<arg type="s" direction="in" name="file_template"/>
<arg type="a{sv}" direction="in" name="options"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<method name="ScreencastArea">
<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="s" direction="in" name="file_template"/>
<arg type="a{sv}" direction="in" name="options"/>
<arg type="b" direction="out" name="success"/>
<arg type="s" direction="out" name="filename_used"/>
</method>
<method name="StopScreencast">
<arg type="b" direction="out" name="success"/>
</method>
</interface>;
const ScreencastIface = '<node> \
<interface name="org.gnome.Shell.Screencast"> \
<method name="Screencast"> \
<arg type="s" direction="in" name="file_template"/> \
<arg type="a{sv}" direction="in" name="options"/> \
<arg type="b" direction="out" name="success"/> \
<arg type="s" direction="out" name="filename_used"/> \
</method> \
<method name="ScreencastArea"> \
<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="s" direction="in" name="file_template"/> \
<arg type="a{sv}" direction="in" name="options"/> \
<arg type="b" direction="out" name="success"/> \
<arg type="s" direction="out" name="filename_used"/> \
</method> \
<method name="StopScreencast"> \
<arg type="b" direction="out" name="success"/> \
</method> \
</interface> \
</node>';
const ScreencastService = new Lang.Class({
Name: 'ScreencastService',
@ -40,13 +41,13 @@ const ScreencastService = new Lang.Class({
Gio.DBus.session.own_name('org.gnome.Shell.Screencast', Gio.BusNameOwnerFlags.REPLACE, null, null);
this._recorders = new Hash.Map();
this._recorders = new Map();
Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
},
get isRecording() {
return this._recorders.size() > 0;
return this._recorders.size > 0;
},
_ensureRecorderForSender: function(sender) {
@ -67,8 +68,7 @@ const ScreencastService = new Lang.Class({
if (Main.sessionMode.allowScreencast)
return;
for (let sender in this._recorders.keys())
this._recorders.delete(sender);
this._recorders.clear();
this.emit('updated');
},
@ -103,8 +103,10 @@ const ScreencastService = new Lang.Class({
ScreencastAsync: function(params, invocation) {
let returnValue = [false, ''];
if (!Main.sessionMode.allowScreencast)
if (!Main.sessionMode.allowScreencast) {
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
let sender = invocation.get_sender();
let recorder = this._ensureRecorderForSender(sender);
@ -122,8 +124,10 @@ const ScreencastService = new Lang.Class({
ScreencastAreaAsync: function(params, invocation) {
let returnValue = [false, ''];
if (!Main.sessionMode.allowScreencast)
if (!Main.sessionMode.allowScreencast) {
invocation.return_value(GLib.Variant.new('(bs)', returnValue));
return;
}
let sender = invocation.get_sender();
let recorder = this._ensureRecorderForSender(sender);
@ -131,6 +135,16 @@ const ScreencastService = new Lang.Class({
if (!recorder.is_recording()) {
let [x, y, width, height, fileTemplate, options] = params;
if (x < 0 || y < 0 ||
width <= 0 || height <= 0 ||
x + width > global.screen_width ||
y + height > global.screen_height) {
invocation.return_error_literal(Gio.IOErrorEnum,
Gio.IOErrorEnum.CANCELLED,
"Invalid params");
return;
}
recorder.set_file_template(fileTemplate);
recorder.set_area(x, y, width, height);
this._applyOptionalParameters(recorder, options);

View File

@ -11,49 +11,52 @@ const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const GrabHelper = imports.ui.grabHelper;
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 ScreenshotIface = '<node> \
<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> \
</node>';
const ScreenshotService = new Lang.Class({
Name: 'ScreenshotService',
@ -77,7 +80,9 @@ const ScreenshotService = new Lang.Class({
ScreenshotAreaAsync : function (params, invocation) {
let [x, y, width, height, flash, filename, callback] = params;
if (height <= 0 || width <= 0) {
if (x < 0 || y < 0 ||
width <= 0 || height <= 0 ||
x + width > global.screen_width || y + height > global.screen_height) {
invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
"Invalid params");
return;
@ -135,6 +140,7 @@ const SelectArea = new Lang.Class({
this._startY = -1;
this._lastX = 0;
this._lastY = 0;
this._result = null;
this._initRubberbandColors();
@ -144,12 +150,12 @@ const SelectArea = new Lang.Class({
y: 0 });
Main.uiGroup.add_actor(this._group);
this._grabHelper = new GrabHelper.GrabHelper(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));
@ -165,10 +171,12 @@ const SelectArea = new Lang.Class({
},
show: function() {
if (!Main.pushModal(this._group) || this._group.visible)
if (!this._grabHelper.grab({ actor: this._group,
onUngrab: Lang.bind(this, this._onUngrab) }))
return;
global.screen.set_cursor(Meta.Cursor.CROSSHAIR);
Main.uiGroup.set_child_above_sibling(this._group, null);
this._group.visible = true;
},
@ -198,16 +206,9 @@ const SelectArea = new Lang.Class({
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;
return Clutter.EVENT_PROPAGATE;
[this._lastX, this._lastY] = event.get_coords();
let geometry = this._getGeometry();
@ -215,35 +216,39 @@ const SelectArea = new Lang.Class({
this._rubberband.set_position(geometry.x, geometry.y);
this._rubberband.set_size(geometry.width, geometry.height);
return false;
return Clutter.EVENT_PROPAGATE;
},
_onButtonPress: function(actor, event) {
[this._startX, this._startY] = event.get_coords();
this._rubberband.set_position(this._startX, this._startY);
return false;
return Clutter.EVENT_PROPAGATE;
},
_onButtonRelease: function(actor, event) {
this._destroy(this._getGeometry(), true);
return false;
},
_destroy: function(geometry, fade) {
this._result = this._getGeometry();
Tweener.addTween(this._group,
{ opacity: 0,
time: fade ? 0.2 : 0,
time: 0.2,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
Main.popModal(this._group);
this._group.destroy();
global.screen.set_cursor(Meta.Cursor.DEFAULT);
this.emit('finished', geometry);
this._grabHelper.ungrab();
})
});
return Clutter.EVENT_PROPAGATE;
},
_onUngrab: function() {
global.screen.set_cursor(Meta.Cursor.DEFAULT);
this.emit('finished', this._result);
GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this,
function() {
this._group.destroy();
return GLib.SOURCE_REMOVE;
}));
}
});
Signals.addSignalMethods(SelectArea.prototype);

View File

@ -1,6 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
@ -38,11 +39,12 @@ const Main = imports.ui.main;
function sleep(milliseconds) {
let cb;
Mainloop.timeout_add(milliseconds, function() {
let id = Mainloop.timeout_add(milliseconds, function() {
if (cb)
cb();
return false;
return GLib.SOURCE_REMOVE;
});
GLib.Source.set_name_by_id(id, '[gnome-shell] sleep');
return function(callback) {
cb = callback;
@ -69,16 +71,18 @@ function waitLeisure() {
};
}
const PerfHelperIface = <interface name="org.gnome.Shell.PerfHelper">
<method name="CreateWindow">
<arg type="i" direction="in" />
<arg type="i" direction="in" />
<arg type="b" direction="in" />
<arg type="b" direction="in" />
</method>
<method name="WaitWindows" />
<method name="DestroyWindows" />
</interface>;
const PerfHelperIface = '<node> \
<interface name="org.gnome.Shell.PerfHelper"> \
<method name="CreateWindow"> \
<arg type="i" direction="in" /> \
<arg type="i" direction="in" /> \
<arg type="b" direction="in" /> \
<arg type="b" direction="in" /> \
</method> \
<method name="WaitWindows" /> \
<method name="DestroyWindows" /> \
</interface> \
</node>';
var PerfHelperProxy = Gio.DBusProxy.makeProxyWrapper(PerfHelperIface);
function PerfHelper() {

View File

@ -1,105 +1,729 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta;
const Signals = imports.signals;
const St = imports.gi.St;
const Atk = imports.gi.Atk;
const AppDisplay = imports.ui.appDisplay;
const DND = imports.ui.dnd;
const IconGrid = imports.ui.iconGrid;
const Main = imports.ui.main;
const Overview = imports.ui.overview;
const RemoteSearch = imports.ui.remoteSearch;
const Separator = imports.ui.separator;
const Util = imports.misc.util;
const SEARCH_PROVIDERS_SCHEMA = 'org.gnome.desktop.search-providers';
const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
const SearchSystem = new Lang.Class({
Name: 'SearchSystem',
_init: function() {
this._providers = [];
this._remoteProviders = [];
this.reset();
this._registerProvider(new AppDisplay.AppSearchProvider());
this._searchSettings = new Gio.Settings({ schema: 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));
this._reloadRemoteProviders();
this._cancellable = new Gio.Cancellable();
},
registerProvider: function (provider) {
provider.searchSystem = this;
addProvider: function(provider) {
this._providers.push(provider);
if (provider.isRemoteProvider)
this._remoteProviders.push(provider);
this.emit('providers-changed');
},
unregisterProvider: function (provider) {
_reloadRemoteProviders: function() {
let remoteProviders = this._providers.filter(function(provider) {
return provider.isRemoteProvider;
});
remoteProviders.forEach(Lang.bind(this, function(provider) {
this._unregisterProvider(provider);
}));
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, function(providers) {
providers.forEach(Lang.bind(this, this._registerProvider));
}));
this.emit('providers-changed');
},
_registerProvider: function (provider) {
this._providers.push(provider);
},
_unregisterProvider: function (provider) {
let index = this._providers.indexOf(provider);
if (index == -1)
return;
provider.searchSystem = null;
this._providers.splice(index, 1);
let remoteIndex = this._remoteProviders.indexOf(provider);
if (remoteIndex != -1)
this._remoteProviders.splice(remoteIndex, 1);
if (provider.display)
provider.display.destroy();
},
getProviders: function() {
return this._providers;
},
getRemoteProviders: function() {
return this._remoteProviders;
},
getTerms: function() {
return this._previousTerms;
return this._terms;
},
reset: function() {
this._previousTerms = [];
this._previousResults = [];
this._terms = [];
this._results = {};
},
setResults: function(provider, results) {
let i = this._providers.indexOf(provider);
if (i == -1)
return;
this._previousResults[i] = [provider, results];
this.emit('search-updated', this._previousResults[i]);
_gotResults: function(results, provider) {
this._results[provider.id] = results;
this.emit('search-updated', provider, results);
},
updateSearchResults: function(terms) {
setTerms: function(terms) {
this._cancellable.cancel();
this._cancellable.reset();
let previousResults = this._results;
let previousTerms = this._terms;
this.reset();
if (!terms)
return;
let searchString = terms.join(' ');
let previousSearchString = this._previousTerms.join(' ');
let previousSearchString = previousTerms.join(' ');
if (searchString == previousSearchString)
return;
let isSubSearch = false;
if (this._previousTerms.length > 0)
if (previousTerms.length > 0)
isSubSearch = searchString.indexOf(previousSearchString) == 0;
let previousResultsArr = this._previousResults;
this._terms = terms;
let results = [];
this._previousTerms = terms;
this._previousResults = results;
if (isSubSearch) {
for (let i = 0; i < this._providers.length; i++) {
let [provider, previousResults] = previousResultsArr[i];
try {
results.push([provider, []]);
provider.getSubsearchResultSet(previousResults, terms);
} catch (error) {
log('A ' + error.name + ' has occured in ' + provider.id + ': ' + error.message);
}
}
} else {
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
try {
results.push([provider, []]);
provider.getInitialResultSet(terms);
} catch (error) {
log('A ' + error.name + ' has occured in ' + provider.id + ': ' + error.message);
}
}
}
this._providers.forEach(Lang.bind(this, function(provider) {
let previousProviderResults = previousResults[provider.id];
if (isSubSearch && previousProviderResults)
provider.getSubsearchResultSet(previousProviderResults, terms, Lang.bind(this, this._gotResults, provider), this._cancellable);
else
provider.getInitialResultSet(terms, Lang.bind(this, this._gotResults, provider), this._cancellable);
}));
}
});
Signals.addSignalMethods(SearchSystem.prototype);
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',
_init: function(provider, metaInfo) {
this.provider = provider;
this.metaInfo = metaInfo;
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.actor.connect('clicked', Lang.bind(this, this.activate));
},
activate: function() {
this.emit('activate', this.metaInfo.id);
},
setSelected: function(selected) {
if (selected)
this.actor.add_style_pseudo_class('selected');
else
this.actor.remove_style_pseudo_class('selected');
}
});
Signals.addSignalMethods(SearchResult.prototype);
const ListSearchResult = new Lang.Class({
Name: 'ListSearchResult',
Extends: SearchResult,
ICON_SIZE: 64,
_init: function(provider, metaInfo) {
this.parent(provider, metaInfo);
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 });
this.actor.label_actor = title;
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) {
this.parent(provider, metaInfo);
this.actor.style_class = 'grid-search-result';
let content = provider.createResultObject(metaInfo);
let dragSource = null;
if (content == null) {
let actor = new St.Bin();
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
{ createIcon: this.metaInfo['createIcon'] });
actor.set_child(icon.actor);
actor.label_actor = icon.label;
dragSource = icon.icon;
content = { actor: actor, icon: icon };
} else {
if (content._delegate && content._delegate.getDragActorSource)
dragSource = content._delegate.getDragActorSource();
}
this.actor.set_child(content.actor);
this.actor.label_actor = content.actor.label_actor;
this.icon = content.icon;
let draggable = DND.makeDraggable(this.actor);
draggable.connect('drag-begin',
Lang.bind(this, function() {
Main.overview.beginItemDrag(this);
}));
draggable.connect('drag-cancelled',
Lang.bind(this, function() {
Main.overview.cancelledItemDrag(this);
}));
draggable.connect('drag-end',
Lang.bind(this, function() {
Main.overview.endItemDrag(this);
}));
if (!dragSource)
// not exactly right, but alignment problems are hard to notice
dragSource = content;
this._dragActorSource = dragSource;
},
getDragActorSource: function() {
return this._dragActorSource;
},
getDragActor: function() {
return this.metaInfo['createIcon'](Main.overview.dashIconSize);
},
shellWorkspaceLaunch: function(params) {
if (this.provider.dragActivateResult)
this.provider.dragActivateResult(this.metaInfo.id, params);
else
this.provider.activateResult(this.metaInfo.id, this.terms);
}
});
const SearchResultsBase = new Lang.Class({
Name: 'SearchResultsBase',
_init: function(provider) {
this.provider = provider;
this._terms = [];
this.actor = new St.BoxLayout({ style_class: 'search-section',
vertical: true });
this._resultDisplayBin = new St.Bin({ x_fill: true,
y_fill: true });
this.actor.add(this._resultDisplayBin, { expand: true });
let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
this.actor.add(separator.actor);
this._resultDisplays = {};
this._cancellable = new Gio.Cancellable();
},
destroy: function() {
this.actor.destroy();
this._terms = [];
},
_clearResultDisplay: function() {
},
clear: function() {
for (let resultId in this._resultDisplays)
this._resultDisplays[resultId].actor.destroy();
this._resultDisplays = {};
this._clearResultDisplay();
this.actor.hide();
},
_keyFocusIn: function(actor) {
this.emit('key-focus-in', actor);
},
_activateResult: function(result, id) {
this.provider.activateResult(id, this._terms);
Main.overview.toggle();
},
_setMoreIconVisible: function(visible) {
},
_ensureResultActors: function(results, callback) {
let metasNeeded = results.filter(Lang.bind(this, function(resultId) {
return this._resultDisplays[resultId] === undefined;
}));
if (metasNeeded.length === 0) {
callback(true);
} else {
this._cancellable.cancel();
this._cancellable.reset();
this.provider.getResultMetas(metasNeeded, Lang.bind(this, function(metas) {
if (metas.length == 0) {
callback(false);
return;
}
if (metas.length != metasNeeded.length) {
log('Wrong number of result metas returned by search provider');
callback(false);
return;
}
metasNeeded.forEach(Lang.bind(this, function(resultId, i) {
let meta = metas[i];
let display = this._createResultDisplay(meta);
display.connect('activate', Lang.bind(this, this._activateResult));
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
this._resultDisplays[resultId] = display;
}));
callback(true);
}), this._cancellable);
}
},
updateSearch: function(providerResults, terms, callback) {
this._terms = terms;
if (providerResults.length == 0) {
this._clearResultDisplay();
this.actor.hide();
callback();
} else {
let maxResults = this._getMaxDisplayedResults();
let results = this.provider.filterResults(providerResults, maxResults);
let hasMoreResults = results.length < providerResults.length;
this._ensureResultActors(results, Lang.bind(this, function(successful) {
this._clearResultDisplay();
if (!successful)
return;
// To avoid CSS transitions causing flickering when
// the first search result stays the same, we hide the
// content while filling in the results.
this.actor.hide();
this._clearResultDisplay();
results.forEach(Lang.bind(this, function(resultId) {
this._addItem(this._resultDisplays[resultId]);
}));
this._setMoreIconVisible(hasMoreResults && this.provider.canLaunchSearch);
this.actor.show();
callback();
}));
}
}
});
const ListSearchResults = new Lang.Class({
Name: 'ListSearchResults',
Extends: SearchResultsBase,
_init: function(provider) {
this.parent(provider);
this._container = new St.BoxLayout({ style_class: 'search-section-content' });
this.providerIcon = new ProviderIcon(provider);
this.providerIcon.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
this.providerIcon.connect('clicked', Lang.bind(this,
function() {
provider.launchSearch(this._terms);
Main.overview.toggle();
}));
this._container.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._container.add(this._content, { expand: true });
this._resultDisplayBin.set_child(this._container);
},
_setMoreIconVisible: function(visible) {
this.providerIcon.moreIcon.visible = true;
},
_getMaxDisplayedResults: function() {
return MAX_LIST_SEARCH_RESULTS_ROWS;
},
_clearResultDisplay: function () {
this._content.remove_all_children();
},
_createResultDisplay: function(meta) {
return new ListSearchResult(this.provider, meta);
},
_addItem: function(display) {
this._content.add_actor(display.actor);
},
getFirstResult: function() {
if (this._content.get_n_children() > 0)
return this._content.get_child_at_index(0)._delegate;
else
return null;
}
});
Signals.addSignalMethods(ListSearchResults.prototype);
const GridSearchResults = new Lang.Class({
Name: 'GridSearchResults',
Extends: SearchResultsBase,
_init: function(provider) {
this.parent(provider);
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START });
this._bin = new St.Bin({ x_align: St.Align.MIDDLE });
this._bin.set_child(this._grid.actor);
this._resultDisplayBin.set_child(this._bin);
},
_getMaxDisplayedResults: function() {
return this._grid.columnsForWidth(this._bin.width) * this._grid.getRowLimit();
},
_renderResults: function(metas) {
for (let i = 0; i < metas.length; i++) {
let display = new GridSearchResult(this.provider, metas[i]);
display.connect('activate', Lang.bind(this, this._activateResult));
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
this._grid.addItem(display);
}
},
_clearResultDisplay: function () {
this._grid.removeAll();
},
_createResultDisplay: function(meta) {
return new GridSearchResult(this.provider, meta);
},
_addItem: function(display) {
this._grid.addItem(display);
},
getFirstResult: function() {
if (this._grid.visibleItemsCount() > 0)
return this._grid.getItemAtIndex(0)._delegate;
else
return null;
}
});
Signals.addSignalMethods(GridSearchResults.prototype);
const SearchResults = new Lang.Class({
Name: 'SearchResults',
_init: function() {
this.actor = new St.BoxLayout({ name: 'searchResults',
vertical: true });
this._content = new St.BoxLayout({ name: 'searchResultsContent',
vertical: true });
this._contentBin = new MaxWidthBin({ name: 'searchResultsBin',
x_fill: true,
y_fill: true,
child: this._content });
let scrollChild = new St.BoxLayout();
scrollChild.add(this._contentBin, { expand: true });
this._scrollView = new St.ScrollView({ x_fill: true,
y_fill: false,
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);
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,
y_align: St.Align.MIDDLE });
this._content.add(this._statusBin, { expand: true });
this._statusBin.add_actor(this._statusText);
this._highlightDefault = false;
this._defaultResult = null;
this._searchSystem = new SearchSystem();
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateResults));
this._searchSystem.connect('providers-changed', Lang.bind(this, this._updateProviderDisplays));
this._updateProviderDisplays();
},
_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;
},
_keyFocusIn: function(provider, actor) {
Util.ensureActorVisibleInScrollView(this._scrollView, actor);
},
_ensureProviderDisplay: function(provider) {
if (provider.display)
return;
let providerDisplay;
if (provider.appInfo)
providerDisplay = new ListSearchResults(provider);
else
providerDisplay = new GridSearchResults(provider);
providerDisplay.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
this._content.add(providerDisplay.actor);
provider.display = providerDisplay;
},
_updateProviderDisplays: function() {
this._searchSystem.getProviders().forEach(Lang.bind(this, this._ensureProviderDisplay));
},
_clearDisplay: function() {
this._searchSystem.getProviders().forEach(function(provider) {
provider.display.clear();
});
},
reset: function() {
this._searchSystem.reset();
this._statusBin.hide();
this._clearDisplay();
this._defaultResult = null;
},
startingSearch: function() {
this.reset();
this._statusText.set_text(_("Searching…"));
this._statusBin.show();
},
setTerms: function(terms) {
this._searchSystem.setTerms(terms);
},
_maybeSetInitialSelection: function() {
let newDefaultResult = null;
let providers = this._searchSystem.getProviders();
for (let i = 0; i < providers.length; i++) {
let provider = providers[i];
let display = provider.display;
if (!display.actor.visible)
continue;
let firstResult = display.getFirstResult();
if (firstResult) {
newDefaultResult = firstResult;
break; // select this one!
}
}
if (newDefaultResult != this._defaultResult) {
if (this._defaultResult)
this._defaultResult.setSelected(false);
if (newDefaultResult) {
newDefaultResult.setSelected(this._highlightDefault);
if (this._highlightDefault)
Util.ensureActorVisibleInScrollView(this._scrollView, newDefaultResult.actor);
}
this._defaultResult = newDefaultResult;
}
},
_updateStatusText: function () {
let haveResults = this._searchSystem.getProviders().some(function(provider) {
let display = provider.display;
return (display.getFirstResult() != null);
});
if (!haveResults) {
this._statusText.set_text(_("No results."));
this._statusBin.show();
} else {
this._statusBin.hide();
}
},
_updateResults: function(searchSystem, provider, results) {
let terms = searchSystem.getTerms();
let display = provider.display;
display.updateSearch(results, terms, Lang.bind(this, function() {
this._maybeSetInitialSelection();
this._updateStatusText();
}));
},
activateDefault: function() {
if (this._defaultResult)
this._defaultResult.activate();
},
highlightDefault: function(highlight) {
this._highlightDefault = highlight;
if (this._defaultResult) {
this._defaultResult.setSelected(highlight);
if (highlight)
Util.ensureActorVisibleInScrollView(this._scrollView, this._defaultResult.actor);
}
},
navigateFocus: function(direction) {
let rtl = this.actor.get_text_direction() == Clutter.TextDirection.RTL;
if (direction == Gtk.DirectionType.TAB_BACKWARD ||
direction == (rtl ? Gtk.DirectionType.RIGHT
: Gtk.DirectionType.LEFT) ||
direction == Gtk.DirectionType.UP) {
this.actor.navigate_focus(null, direction, false);
return;
}
let from = this._defaultResult ? this._defaultResult.actor : null;
this.actor.navigate_focus(from, 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,
accessible_name: provider.appInfo.get_name(),
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);
}
});

View File

@ -1,566 +0,0 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta;
const Signals = imports.signals;
const St = imports.gi.St;
const Atk = imports.gi.Atk;
const 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 Util = imports.misc.util;
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',
_init: function(provider, metaInfo, terms) {
this.provider = provider;
this.metaInfo = metaInfo;
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.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 });
this.actor.label_actor = title;
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.createResultObject(metaInfo, terms);
let dragSource = null;
if (content == null) {
let actor = new St.Bin();
let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
{ createIcon: this.metaInfo['createIcon'] });
actor.set_child(icon.actor);
actor.label_actor = icon.label;
dragSource = icon.icon;
content = { actor: actor, icon: icon };
} else {
if (content._delegate && content._delegate.getDragActorSource)
dragSource = content._delegate.getDragActorSource();
}
this.actor.set_child(content.actor);
this.actor.label_actor = content.actor.label_actor;
this.icon = content.icon;
let draggable = DND.makeDraggable(this.actor);
draggable.connect('drag-begin',
Lang.bind(this, function() {
Main.overview.beginItemDrag(this);
}));
draggable.connect('drag-cancelled',
Lang.bind(this, function() {
Main.overview.cancelledItemDrag(this);
}));
draggable.connect('drag-end',
Lang.bind(this, function() {
Main.overview.endItemDrag(this);
}));
if (!dragSource)
// not exactly right, but alignment problems are hard to notice
dragSource = content;
this._dragActorSource = dragSource;
},
getDragActorSource: function() {
return this._dragActorSource;
},
getDragActor: function() {
return this.metaInfo['createIcon'](Main.overview.dashIconSize);
},
shellWorkspaceLaunch: function(params) {
if (this.provider.dragActivateResult)
this.provider.dragActivateResult(this.metaInfo.id, params);
else
this.provider.activateResult(this.metaInfo.id, this.terms);
}
});
const SearchResultsBase = new Lang.Class({
Name: 'SearchResultsBase',
_init: function(provider) {
this.provider = provider;
this._terms = [];
this.actor = new St.BoxLayout({ style_class: 'search-section',
vertical: true });
this._resultDisplayBin = new St.Bin({ x_fill: true,
y_fill: true });
this.actor.add(this._resultDisplayBin, { expand: true });
let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
this.actor.add(separator.actor);
},
destroy: function() {
this.actor.destroy();
this._terms = [];
},
_clearResultDisplay: function() {
},
clear: function() {
this._clearResultDisplay();
this.actor.hide();
},
_keyFocusIn: function(icon) {
this.emit('key-focus-in', icon);
},
_setMoreIconVisible: function(visible) {
},
updateSearch: function(providerResults, terms, callback) {
this._terms = terms;
if (providerResults.length == 0) {
this._clearResultDisplay();
this.actor.hide();
callback();
} else {
let maxResults = this._getMaxDisplayedResults();
let results = providerResults.slice(0, maxResults);
let hasMoreResults = results.length < providerResults.length;
this.provider.getResultMetas(results, Lang.bind(this, function(metas) {
this.clear();
// To avoid CSS transitions causing flickering when
// the first search result stays the same, we hide the
// content while filling in the results.
this.actor.hide();
this._clearResultDisplay();
this._renderResults(metas);
this._setMoreIconVisible(hasMoreResults && this.provider.canLaunchSearch);
this.actor.show();
callback();
}));
}
}
});
const ListSearchResults = new Lang.Class({
Name: 'ListSearchResults',
Extends: SearchResultsBase,
_init: function(provider) {
this.parent(provider);
this._container = 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._container.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._container.add(this._content, { expand: true });
this._resultDisplayBin.set_child(this._container);
},
_setMoreIconVisible: function(visible) {
this.providerIcon.moreIcon.visible = true;
},
_getMaxDisplayedResults: function() {
return MAX_LIST_SEARCH_RESULTS_ROWS;
},
_renderResults: function(metas) {
for (let i = 0; i < metas.length; i++) {
let display = new ListSearchResult(this.provider, metas[i], this._terms);
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
this._content.add_actor(display.actor);
}
},
_clearResultDisplay: function () {
this._content.destroy_all_children();
},
getFirstResult: function() {
if (this._content.get_n_children() > 0)
return this._content.get_child_at_index(0)._delegate;
else
return null;
}
});
Signals.addSignalMethods(ListSearchResults.prototype);
const GridSearchResults = new Lang.Class({
Name: 'GridSearchResults',
Extends: SearchResultsBase,
_init: function(provider) {
this.parent(provider);
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START });
this._bin = new St.Bin({ x_align: St.Align.MIDDLE });
this._bin.set_child(this._grid.actor);
this._resultDisplayBin.set_child(this._bin);
},
_getMaxDisplayedResults: function() {
return this._grid.columnsForWidth(this._bin.width) * this._grid.getRowLimit();
},
_renderResults: function(metas) {
for (let i = 0; i < metas.length; i++) {
let display = new GridSearchResult(this.provider, metas[i], this._terms);
display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
this._grid.addItem(display);
}
},
_clearResultDisplay: function () {
this._grid.removeAll();
},
getFirstResult: function() {
if (this._grid.visibleItemsCount() > 0)
return this._grid.getItemAtIndex(0)._delegate;
else
return null;
}
});
Signals.addSignalMethods(GridSearchResults.prototype);
const SearchResults = new Lang.Class({
Name: 'SearchResults',
_init: function(searchSystem) {
this._searchSystem = searchSystem;
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateResults));
this.actor = new St.BoxLayout({ name: 'searchResults',
vertical: true });
this._content = new St.BoxLayout({ name: 'searchResultsContent',
vertical: true });
this._contentBin = new MaxWidthBin({ name: 'searchResultsBin',
x_fill: true,
y_fill: true,
child: this._content });
let scrollChild = new St.BoxLayout();
scrollChild.add(this._contentBin, { expand: true });
this._scrollView = new St.ScrollView({ x_fill: true,
y_fill: false,
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);
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,
y_align: St.Align.MIDDLE });
this._content.add(this._statusBin, { expand: true });
this._statusBin.add_actor(this._statusText);
this._providers = this._searchSystem.getProviders();
this._providerDisplays = {};
for (let i = 0; i < this._providers.length; i++) {
this.createProviderDisplay(this._providers[i]);
}
this._highlightDefault = false;
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;
},
_keyFocusIn: function(provider, icon) {
Util.ensureActorVisibleInScrollView(this._scrollView, icon);
},
createProviderDisplay: function(provider) {
let providerDisplay = null;
if (provider.appInfo) {
providerDisplay = new ListSearchResults(provider);
} else {
providerDisplay = new GridSearchResults(provider);
}
providerDisplay.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
this._providerDisplays[provider.id] = providerDisplay;
this._content.add(providerDisplay.actor);
},
destroyProviderDisplay: function(provider) {
this._providerDisplays[provider.id].destroy();
delete this._providerDisplays[provider.id];
},
_clearDisplay: function() {
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
let providerDisplay = this._providerDisplays[provider.id];
providerDisplay.clear();
}
},
reset: function() {
this._searchSystem.reset();
this._statusBin.hide();
this._clearDisplay();
this._defaultResult = null;
},
startingSearch: function() {
this.reset();
this._statusText.set_text(_("Searching…"));
this._statusBin.show();
},
_maybeSetInitialSelection: function() {
let newDefaultResult = null;
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
let display = this._providerDisplays[provider.id];
if (!display.actor.visible)
continue;
let firstResult = display.getFirstResult();
if (firstResult) {
newDefaultResult = firstResult;
break; // select this one!
}
}
if (newDefaultResult != this._defaultResult) {
if (this._defaultResult)
this._defaultResult.setSelected(false);
if (newDefaultResult)
newDefaultResult.setSelected(this._highlightDefault);
this._defaultResult = newDefaultResult;
}
},
_updateStatusText: function () {
let haveResults = false;
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
let display = this._providerDisplays[provider.id];
if (display.getFirstResult()) {
haveResults = true;
break;
}
}
if (!haveResults) {
this._statusText.set_text(_("No results."));
this._statusBin.show();
} else {
this._statusBin.hide();
}
},
_updateResults: function(searchSystem, results) {
let terms = searchSystem.getTerms();
let [provider, providerResults] = results;
let display = this._providerDisplays[provider.id];
display.updateSearch(providerResults, terms, Lang.bind(this, function() {
this._maybeSetInitialSelection();
this._updateStatusText();
}));
},
activateDefault: function() {
if (this._defaultResult)
this._defaultResult.activate();
},
highlightDefault: function(highlight) {
this._highlightDefault = highlight;
if (this._defaultResult)
this._defaultResult.setSelected(highlight);
},
navigateFocus: function(direction) {
let rtl = this.actor.get_text_direction() == Clutter.TextDirection.RTL;
if (direction == Gtk.DirectionType.TAB_BACKWARD ||
direction == (rtl ? Gtk.DirectionType.RIGHT
: Gtk.DirectionType.LEFT) ||
direction == Gtk.DirectionType.UP) {
this.actor.navigate_focus(null, direction, false);
return;
}
let from = this._defaultResult ? this._defaultResult.actor : null;
this.actor.navigate_focus(from, 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,
accessible_name: provider.appInfo.get_name(),
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);
}
});

View File

@ -10,6 +10,8 @@ const FileUtils = imports.misc.fileUtils;
const Main = imports.ui.main;
const Params = imports.misc.params;
const Config = imports.misc.config;
const DEFAULT_MODE = 'restrictive';
const _modes = {
@ -92,8 +94,12 @@ const _modes = {
isLocked: false,
isPrimary: true,
unlockDialog: imports.ui.unlockDialog.UnlockDialog,
components: ['networkAgent', 'polkitAgent', 'telepathyClient',
components: Config.HAVE_NETWORKMANAGER ?
['networkAgent', 'polkitAgent', 'telepathyClient',
'keyring', 'autorunManager', 'automountManager'] :
['polkitAgent', 'telepathyClient',
'keyring', 'autorunManager', 'automountManager'],
panel: {
left: ['activities', 'appMenu'],
center: ['dateMenu'],
@ -102,19 +108,12 @@ const _modes = {
}
};
function _getModes(modesLoadedCallback) {
FileUtils.collectFromDatadirsAsync('modes',
{ processFile: _loadMode,
loadedCallback: modesLoadedCallback,
data: _modes });
}
function _loadMode(file, info, loadedData) {
function _loadMode(file, info) {
let name = info.get_name();
let suffix = name.indexOf('.json');
let modeName = suffix == -1 ? name : name.slice(name, suffix);
if (loadedData.hasOwnProperty(modeName))
if (_modes.hasOwnProperty(modeName))
return;
let fileContent, success, tag, newMode;
@ -125,41 +124,43 @@ function _loadMode(file, info, loadedData) {
return;
}
loadedData[modeName] = {};
_modes[modeName] = {};
let propBlacklist = ['unlockDialog'];
for (let prop in loadedData[DEFAULT_MODE]) {
for (let prop in _modes[DEFAULT_MODE]) {
if (newMode[prop] !== undefined &&
propBlacklist.indexOf(prop) == -1)
loadedData[modeName][prop]= newMode[prop];
_modes[modeName][prop] = newMode[prop];
}
loadedData[modeName]['isPrimary'] = true;
_modes[modeName]['isPrimary'] = true;
}
function _loadModes() {
FileUtils.collectFromDatadirs('modes', false, _loadMode);
}
function listModes() {
_getModes(function(modes) {
let names = Object.getOwnPropertyNames(modes);
_loadModes();
let id = Mainloop.idle_add(function() {
let names = Object.getOwnPropertyNames(_modes);
for (let i = 0; i < names.length; i++)
if (_modes[names[i]].isPrimary)
print(names[i]);
Mainloop.quit('listModes');
});
GLib.Source.set_name_by_id(id, '[gnome-shell] listModes');
Mainloop.run('listModes');
}
const SessionMode = new Lang.Class({
Name: 'SessionMode',
init: function() {
_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();
this.emit('sessions-loaded');
}));
_init: function() {
_loadModes();
let isPrimary = (_modes[global.session_mode] &&
_modes[global.session_mode].isPrimary);
let mode = isPrimary ? global.session_mode : 'user';
this._modeStack = [mode];
this._sync();
},
pushMode: function(mode) {
@ -186,13 +187,13 @@ const SessionMode = new Lang.Class({
},
_sync: function() {
let params = this._modes[this.currentMode];
let params = _modes[this.currentMode];
let defaults;
if (params.parentMode)
defaults = Params.parse(this._modes[params.parentMode],
this._modes[DEFAULT_MODE]);
defaults = Params.parse(_modes[params.parentMode],
_modes[DEFAULT_MODE]);
else
defaults = this._modes[DEFAULT_MODE];
defaults = _modes[DEFAULT_MODE];
params = Params.parse(params, defaults);
// A simplified version of Lang.copyProperties, handles

View File

@ -10,64 +10,68 @@ const Config = imports.misc.config;
const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionDownloader = imports.ui.extensionDownloader;
const ExtensionUtils = imports.misc.extensionUtils;
const Hash = imports.misc.hash;
const Main = imports.ui.main;
const Screenshot = imports.ui.screenshot;
const ViewSelector = imports.ui.viewSelector;
const GnomeShellIface = <interface name="org.gnome.Shell">
<method name="Eval">
<arg type="s" direction="in" name="script" />
<arg type="b" direction="out" name="success" />
<arg type="s" direction="out" name="result" />
</method>
<method name="FocusSearch"/>
<method name="ShowOSD">
<arg type="a{sv}" direction="in" name="params"/>
</method>
<method name="FocusApp">
<arg type="s" direction="in" name="id"/>
</method>
<method name="ShowApplications" />
<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>
<signal name="AcceleratorActivated">
<arg name="action" type="u" />
<arg name="deviceid" type="u" />
<arg name="timestamp" 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>;
const GnomeShellIface = '<node> \
<interface name="org.gnome.Shell"> \
<method name="Eval"> \
<arg type="s" direction="in" name="script" /> \
<arg type="b" direction="out" name="success" /> \
<arg type="s" direction="out" name="result" /> \
</method> \
<method name="FocusSearch"/> \
<method name="ShowOSD"> \
<arg type="a{sv}" direction="in" name="params"/> \
</method> \
<method name="FocusApp"> \
<arg type="s" direction="in" name="id"/> \
</method> \
<method name="ShowApplications" /> \
<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> \
<signal name="AcceleratorActivated"> \
<arg name="action" type="u" /> \
<arg name="deviceid" type="u" /> \
<arg name="timestamp" 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> \
</node>';
const ScreenSaverIface = <interface name="org.gnome.ScreenSaver">
<method name="Lock">
</method>
<method name="GetActive">
<arg name="active" direction="out" type="b" />
</method>
<method name="SetActive">
<arg name="value" direction="in" type="b" />
</method>
<method name="GetActiveTime">
<arg name="value" direction="out" type="u" />
</method>
<signal name="ActiveChanged">
<arg name="new_value" type="b" />
</signal>
</interface>;
const ScreenSaverIface = '<node> \
<interface name="org.gnome.ScreenSaver"> \
<method name="Lock"> \
</method> \
<method name="GetActive"> \
<arg name="active" direction="out" type="b" /> \
</method> \
<method name="SetActive"> \
<arg name="value" direction="in" type="b" /> \
</method> \
<method name="GetActiveTime"> \
<arg name="value" direction="out" type="u" /> \
</method> \
<signal name="ActiveChanged"> \
<arg name="new_value" type="b" /> \
</signal> \
<signal name="WakeUpScreen" /> \
</interface> \
</node>';
const GnomeShell = new Lang.Class({
Name: 'GnomeShellDBus',
@ -79,8 +83,8 @@ const GnomeShell = new Lang.Class({
this._extensionsService = new GnomeShellExtensions();
this._screenshotService = new Screenshot.ScreenshotService();
this._grabbedAccelerators = new Hash.Map();
this._grabbers = new Hash.Map();
this._grabbedAccelerators = new Map();
this._grabbers = new Map();
global.display.connect('accelerator-activated', Lang.bind(this,
function(display, action, deviceid, timestamp) {
@ -115,7 +119,7 @@ const GnomeShell = new Lang.Class({
returnValue = '';
success = true;
} catch (e) {
returnValue = JSON.stringify(e);
returnValue = '' + e;
success = false;
}
return [success, returnValue];
@ -129,15 +133,15 @@ const GnomeShell = new Lang.Class({
for (let param in params)
params[param] = params[param].deep_unpack();
let monitorIndex = -1;
if (params['monitor'])
monitorIndex = params['monitor'];
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();
Main.osdWindowManager.show(monitorIndex, icon, params['label'], params['level']);
},
FocusApp: function(id) {
@ -219,9 +223,8 @@ const GnomeShell = new Lang.Class({
},
_onGrabberBusNameVanished: function(connection, name) {
let grabs = this._grabbedAccelerators.items();
for (let i = 0; i < grabs.length; i++) {
let [action, sender] = grabs[i];
let grabs = this._grabbedAccelerators.entries();
for (let [action, sender] of grabs) {
if (sender == name)
this._ungrabAccelerator(action);
}
@ -246,41 +249,43 @@ const GnomeShell = new Lang.Class({
ShellVersion: Config.PACKAGE_VERSION
});
const GnomeShellExtensionsIface = <interface name="org.gnome.Shell.Extensions">
<method name="ListExtensions">
<arg type="a{sa{sv}}" direction="out" name="extensions" />
</method>
<method name="GetExtensionInfo">
<arg type="s" direction="in" name="extension" />
<arg type="a{sv}" direction="out" name="info" />
</method>
<method name="GetExtensionErrors">
<arg type="s" direction="in" name="extension" />
<arg type="as" direction="out" name="errors" />
</method>
<signal name="ExtensionStatusChanged">
<arg type="s" name="uuid"/>
<arg type="i" name="state"/>
<arg type="s" name="error"/>
</signal>
<method name="InstallRemoteExtension">
<arg type="s" direction="in" name="uuid"/>
<arg type="s" direction="out" name="result"/>
</method>
<method name="UninstallExtension">
<arg type="s" direction="in" name="uuid"/>
<arg type="b" direction="out" name="success"/>
</method>
<method name="LaunchExtensionPrefs">
<arg type="s" direction="in" name="uuid"/>
</method>
<method name="ReloadExtension">
<arg type="s" direction="in" name="uuid"/>
</method>
<method name="CheckForUpdates">
</method>
<property name="ShellVersion" type="s" access="read" />
</interface>;
const GnomeShellExtensionsIface = '<node> \
<interface name="org.gnome.Shell.Extensions"> \
<method name="ListExtensions"> \
<arg type="a{sa{sv}}" direction="out" name="extensions" /> \
</method> \
<method name="GetExtensionInfo"> \
<arg type="s" direction="in" name="extension" /> \
<arg type="a{sv}" direction="out" name="info" /> \
</method> \
<method name="GetExtensionErrors"> \
<arg type="s" direction="in" name="extension" /> \
<arg type="as" direction="out" name="errors" /> \
</method> \
<signal name="ExtensionStatusChanged"> \
<arg type="s" name="uuid"/> \
<arg type="i" name="state"/> \
<arg type="s" name="error"/> \
</signal> \
<method name="InstallRemoteExtension"> \
<arg type="s" direction="in" name="uuid"/> \
<arg type="s" direction="out" name="result"/> \
</method> \
<method name="UninstallExtension"> \
<arg type="s" direction="in" name="uuid"/> \
<arg type="b" direction="out" name="success"/> \
</method> \
<method name="LaunchExtensionPrefs"> \
<arg type="s" direction="in" name="uuid"/> \
</method> \
<method name="ReloadExtension"> \
<arg type="s" direction="in" name="uuid"/> \
</method> \
<method name="CheckForUpdates"> \
</method> \
<property name="ShellVersion" type="s" access="read" /> \
</interface> \
</node>';
const GnomeShellExtensions = new Lang.Class({
Name: 'GnomeShellExtensionsDBus',
@ -362,8 +367,10 @@ const GnomeShellExtensions = new Lang.Class({
LaunchExtensionPrefs: function(uuid) {
let appSys = Shell.AppSystem.get_default();
let app = appSys.lookup_app('gnome-shell-extension-prefs.desktop');
app.launch(global.display.get_current_time_roundtrip(),
['extension:///' + uuid], -1, null);
let info = app.get_app_info();
let timestamp = global.display.get_current_time_roundtrip();
info.launch_uris(['extension:///' + uuid],
global.create_app_launch_context(timestamp, -1));
},
ReloadExtension: function(uuid) {
@ -396,6 +403,9 @@ const ScreenSaverDBus = new Lang.Class({
screenShield.connect('active-changed', Lang.bind(this, function(shield) {
this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.active]));
}));
screenShield.connect('wake-up-screen', Lang.bind(this, function(shield) {
this._dbusImpl.emit_signal('WakeUpScreen', null);
}));
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/ScreenSaver');

View File

@ -132,14 +132,14 @@ function _setMenuAlignment(entry, stageX) {
function _onButtonPressEvent(actor, event, entry) {
if (entry.menu.isOpen) {
entry.menu.close(BoxPointer.PopupAnimation.FULL);
return true;
return Clutter.EVENT_STOP;
} else if (event.get_button() == 3) {
let [stageX, stageY] = event.get_coords();
_setMenuAlignment(entry, stageX);
entry.menu.open(BoxPointer.PopupAnimation.FULL);
return true;
return Clutter.EVENT_STOP;
}
return false;
return Clutter.EVENT_PROPAGATE;
};
function _onPopup(actor, entry) {

View File

@ -521,36 +521,38 @@ const ShellProcessesDialog = new Lang.Class({
});
Signals.addSignalMethods(ShellProcessesDialog.prototype);
const GnomeShellMountOpIface = <interface name="org.Gtk.MountOperationHandler">
<method name="AskPassword">
<arg type="s" direction="in" name="object_id"/>
<arg type="s" direction="in" name="message"/>
<arg type="s" direction="in" name="icon_name"/>
<arg type="s" direction="in" name="default_user"/>
<arg type="s" direction="in" name="default_domain"/>
<arg type="u" direction="in" name="flags"/>
<arg type="u" direction="out" name="response"/>
<arg type="a{sv}" direction="out" name="response_details"/>
</method>
<method name="AskQuestion">
<arg type="s" direction="in" name="object_id"/>
<arg type="s" direction="in" name="message"/>
<arg type="s" direction="in" name="icon_name"/>
<arg type="as" direction="in" name="choices"/>
<arg type="u" direction="out" name="response"/>
<arg type="a{sv}" direction="out" name="response_details"/>
</method>
<method name="ShowProcesses">
<arg type="s" direction="in" name="object_id"/>
<arg type="s" direction="in" name="message"/>
<arg type="s" direction="in" name="icon_name"/>
<arg type="ai" direction="in" name="application_pids"/>
<arg type="as" direction="in" name="choices"/>
<arg type="u" direction="out" name="response"/>
<arg type="a{sv}" direction="out" name="response_details"/>
</method>
<method name="Close"/>
</interface>;
const GnomeShellMountOpIface = '<node> \
<interface name="org.Gtk.MountOperationHandler"> \
<method name="AskPassword"> \
<arg type="s" direction="in" name="object_id"/> \
<arg type="s" direction="in" name="message"/> \
<arg type="s" direction="in" name="icon_name"/> \
<arg type="s" direction="in" name="default_user"/> \
<arg type="s" direction="in" name="default_domain"/> \
<arg type="u" direction="in" name="flags"/> \
<arg type="u" direction="out" name="response"/> \
<arg type="a{sv}" direction="out" name="response_details"/> \
</method> \
<method name="AskQuestion"> \
<arg type="s" direction="in" name="object_id"/> \
<arg type="s" direction="in" name="message"/> \
<arg type="s" direction="in" name="icon_name"/> \
<arg type="as" direction="in" name="choices"/> \
<arg type="u" direction="out" name="response"/> \
<arg type="a{sv}" direction="out" name="response_details"/> \
</method> \
<method name="ShowProcesses"> \
<arg type="s" direction="in" name="object_id"/> \
<arg type="s" direction="in" name="message"/> \
<arg type="s" direction="in" name="icon_name"/> \
<arg type="ai" direction="in" name="application_pids"/> \
<arg type="as" direction="in" name="choices"/> \
<arg type="u" direction="out" name="response"/> \
<arg type="a{sv}" direction="out" name="response_details"/> \
</method> \
<method name="Close"/> \
</interface> \
</node>';
const ShellMountOperationType = {
NONE: 0,

View File

@ -1,11 +1,11 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Atk = imports.gi.Atk;
const Cairo = imports.cairo;
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const St = imports.gi.St;
const Signals = imports.signals;
const Atk = imports.gi.Atk;
const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */
@ -111,12 +111,12 @@ const Slider = new Lang.Class({
},
_startDragging: function(actor, event) {
this.startDragging(event);
return this.startDragging(event);
},
startDragging: function(event) {
if (this._dragging)
return false;
return Clutter.EVENT_PROPAGATE;
this._dragging = true;
@ -129,7 +129,7 @@ const Slider = new Lang.Class({
let absX, absY;
[absX, absY] = event.get_coords();
this._moveHandle(absX, absY);
return true;
return Clutter.EVENT_STOP;
},
_endDragging: function() {
@ -143,7 +143,7 @@ const Slider = new Lang.Class({
this.emit('drag-end');
}
return true;
return Clutter.EVENT_STOP;
},
scroll: function(event) {
@ -151,7 +151,7 @@ const Slider = new Lang.Class({
let delta;
if (event.is_pointer_emulated())
return;
return Clutter.EVENT_PROPAGATE;
if (direction == Clutter.ScrollDirection.DOWN) {
delta = -SLIDER_SCROLL_STEP;
@ -168,17 +168,18 @@ const Slider = new Lang.Class({
this.actor.queue_repaint();
this.emit('value-changed', this._value);
return Clutter.EVENT_STOP;
},
_onScrollEvent: function(actor, event) {
this.scroll(event);
return this.scroll(event);
},
_motionEvent: function(actor, event) {
let absX, absY;
[absX, absY] = event.get_coords();
this._moveHandle(absX, absY);
return true;
return Clutter.EVENT_STOP;
},
onKeyPressEvent: function (actor, event) {
@ -189,9 +190,9 @@ const Slider = new Lang.Class({
this.actor.queue_repaint();
this.emit('value-changed', this._value);
this.emit('drag-end');
return true;
return Clutter.EVENT_STOP;
}
return false;
return Clutter.EVENT_PROPAGATE;
},
_moveHandle: function(absX, absY) {

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 Mainloop = imports.mainloop;
const St = imports.gi.St;
@ -43,9 +44,7 @@ const ATIndicator = new Lang.Class({
this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
this._hbox.add_child(new St.Icon({ style_class: 'system-status-icon',
icon_name: 'preferences-desktop-accessibility-symbolic' }));
this._hbox.add_child(new St.Label({ text: '\u25BE',
y_expand: true,
y_align: Clutter.ActorAlign.CENTER }));
this._hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
this.actor.add_child(this._hbox);
@ -96,7 +95,7 @@ const ATIndicator = new Lang.Class({
this.actor.visible = alwaysShow || items.some(function(f) { return !!f.state; });
return false;
return GLib.SOURCE_REMOVE;
},
_queueSyncMenuVisibility: function() {
@ -104,6 +103,7 @@ const ATIndicator = new Lang.Class({
return;
this._syncMenuVisbilityIdle = Mainloop.idle_add(Lang.bind(this, this._syncMenuVisibility));
GLib.Source.set_name_by_id(this._syncMenuVisbilityIdle, '[gnome-shell] this._syncMenuVisibility');
},
_buildItemExtended: function(string, initial_value, writable, on_set) {

View File

@ -2,17 +2,27 @@
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const GnomeBluetoothApplet = imports.gi.GnomeBluetoothApplet;
const Gio = imports.gi.Gio;
const GnomeBluetooth = imports.gi.GnomeBluetooth;
const Lang = imports.lang;
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;
const BUS_NAME = 'org.gnome.SettingsDaemon.Rfkill';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Rfkill';
const RfkillManagerInterface = '<node> \
<interface name="org.gnome.SettingsDaemon.Rfkill"> \
<property name="BluetoothAirplaneMode" type="b" access="readwrite" /> \
<property name="BluetoothHasAirplaneMode" type="b" access="read" /> \
</interface> \
</node>';
const RfkillManagerProxy = Gio.DBusProxy.makeProxyWrapper(RfkillManagerInterface);
const Indicator = new Lang.Class({
Name: 'BTIndicator',
Extends: PanelMenu.SystemIndicator,
@ -23,237 +33,76 @@ const Indicator = new Lang.Class({
this._indicator = this._addIndicator();
this._indicator.icon_name = 'bluetooth-active-symbolic';
this._proxy = new RfkillManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
Lang.bind(this, function(proxy, error) {
if (error) {
log(error.message);
return;
}
this._sync();
}));
this._proxy.connect('g-properties-changed', Lang.bind(this, this._sync));
// The Bluetooth menu only appears when Bluetooth is in use,
// so just statically build it with a "Turn Off" menu item.
this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true);
this._item.icon.icon_name = 'bluetooth-active-symbolic';
this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() {
this._applet.killswitch_state = GnomeBluetooth.KillswitchState.SOFT_BLOCKED;
this._proxy.BluetoothAirplaneMode = true;
}));
this._item.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
this.menu.addMenuItem(this._item);
this._applet = new GnomeBluetoothApplet.Applet();
this._applet.connect('devices-changed', Lang.bind(this, this._sync));
this._client = new GnomeBluetooth.Client();
this._model = this._client.get_model();
this._model.connect('row-changed', Lang.bind(this, this._sync));
this._model.connect('row-deleted', Lang.bind(this, this._sync));
this._model.connect('row-inserted', Lang.bind(this, this._sync));
Main.sessionMode.connect('updated', Lang.bind(this, this._sync));
this._sync();
},
this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
this._applet.connect('auth-request', Lang.bind(this, this._authRequest));
this._applet.connect('auth-service-request', Lang.bind(this, this._authServiceRequest));
this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest));
_getDefaultAdapter: function() {
let [ret, iter] = this._model.get_iter_first();
while (ret) {
let isDefault = this._model.get_value(iter,
GnomeBluetooth.Column.DEFAULT);
if (isDefault)
return iter;
ret = this._model.iter_next(iter);
}
return null;
},
_getNConnectedDevices: function() {
let adapter = this._getDefaultAdapter();
if (!adapter)
return 0;
let nDevices = 0;
let [ret, iter] = this._model.iter_children(adapter);
while (ret) {
let isConnected = this._model.get_value(iter,
GnomeBluetooth.Column.CONNECTED);
if (isConnected)
nDevices++;
ret = this._model.iter_next(iter);
}
return nDevices;
},
_sync: function() {
let connectedDevices = this._applet.get_devices().filter(function(device) {
return device.connected;
});
let nDevices = connectedDevices.length;
let nDevices = this._getNConnectedDevices();
let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
let on = nDevices > 0;
this._indicator.visible = on;
this._item.actor.visible = on;
this.menu.setSensitive(sensitive);
this._indicator.visible = nDevices > 0;
this._item.actor.visible = this._proxy.BluetoothHasAirplaneMode && !this._proxy.BluetoothAirplaneMode;
if (on)
this._item.status.text = ngettext("%d Connected Device", "%d Connected Devices").format(nDevices);
},
_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);
}
},
_authRequest: function(applet, device_path, name, long_name) {
this._ensureSource();
this._source.notify(new AuthNotification(this._source, this._applet, device_path, name, long_name));
},
_authServiceRequest: function(applet, device_path, name, long_name, uuid) {
this._ensureSource();
this._source.notify(new AuthServiceNotification(this._source, this._applet, device_path, name, long_name, uuid));
},
_confirmRequest: function(applet, device_path, name, long_name, pin) {
this._ensureSource();
this._source.notify(new ConfirmNotification(this._source, this._applet, device_path, name, long_name, pin));
},
_pinRequest: function(applet, device_path, name, long_name, numeric) {
this._ensureSource();
this._source.notify(new PinNotification(this._source, this._applet, device_path, name, long_name, numeric));
},
_cancelRequest: function() {
this._source.destroy();
}
});
const AuthNotification = new Lang.Class({
Name: 'AuthNotification',
Extends: MessageTray.Notification,
_init: function(source, applet, device_path, name, long_name) {
this.parent(source,
_("Bluetooth"),
_("Authorization request from %s").format(name),
{ customContent: true });
this.setResident(true);
this._applet = applet;
this._devicePath = device_path;
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
this.addButton('allow', _("Allow"));
this.addButton('deny', _("Deny"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
if (action == 'allow')
this._applet.agent_reply_confirm(this._devicePath, true);
else
this._applet.agent_reply_confirm(this._devicePath, false);
this.destroy();
}));
}
});
const AuthServiceNotification = new Lang.Class({
Name: 'AuthServiceNotification',
Extends: MessageTray.Notification,
_init: function(source, applet, device_path, name, long_name, uuid) {
this.parent(source,
_("Bluetooth"),
_("Authorization request from %s").format(name),
{ customContent: true });
this.setResident(true);
this._applet = applet;
this._devicePath = device_path;
this.addBody(_("Device %s wants access to the service '%s'").format(long_name, uuid));
this.addButton('always-grant', _("Always grant access"));
this.addButton('grant', _("Grant this time only"));
this.addButton('reject', _("Reject"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'always-grant':
this._applet.agent_reply_auth_service(this._devicePath, true, true);
break;
case 'grant':
this._applet.agent_reply_auth_service(this._devicePath, true, false);
break;
case 'reject':
default:
this._applet.agent_reply_auth_service(this._devicePath, false, false);
}
this.destroy();
}));
}
});
const ConfirmNotification = new Lang.Class({
Name: 'ConfirmNotification',
Extends: MessageTray.Notification,
_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);
this._applet = applet;
this._devicePath = device_path;
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
this.addBody(_("Please confirm whether the Passkey '%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"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
if (action == 'matches')
this._applet.agent_reply_confirm(this._devicePath, true);
else
this._applet.agent_reply_confirm(this._devicePath, false);
this.destroy();
}));
}
});
const PinNotification = new Lang.Class({
Name: 'PinNotification',
Extends: MessageTray.Notification,
_init: function(source, applet, device_path, name, long_name, numeric) {
this.parent(source,
_("Bluetooth"),
_("Pairing request for %s").format(name),
{ customContent: true });
this.setResident(true);
this._applet = applet;
this._devicePath = device_path;
this._numeric = numeric;
this.addBody(_("Device %s wants to pair with this computer").format(long_name));
this.addBody(_("Please enter the PIN mentioned on the device."));
this._entry = new St.Entry();
this._entry.connect('key-release-event', Lang.bind(this, function(entry, event) {
let key = event.get_key_symbol();
if (key == Clutter.KEY_Return) {
if (this._canActivateOkButton())
this.emit('action-invoked', 'ok');
return true;
} else if (key == Clutter.KEY_Escape) {
this.emit('action-invoked', 'cancel');
return true;
}
return false;
}));
this.addActor(this._entry);
this.addButton('ok', _("OK"));
this.addButton('cancel', _("Cancel"));
this.setButtonSensitive('ok', this._canActivateOkButton());
this._entry.clutter_text.connect('text-changed', Lang.bind(this,
function() {
this.setButtonSensitive('ok', this._canActivateOkButton());
}));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
if (action == 'ok') {
if (this._numeric) {
let num = parseInt(this._entry.text);
if (isNaN(num)) {
// user reply was empty, or was invalid
// cancel the operation
num = -1;
}
this._applet.agent_reply_passkey(this._devicePath, num);
} else
this._applet.agent_reply_pincode(this._devicePath, this._entry.text);
} else {
if (this._numeric)
this._applet.agent_reply_passkey(this._devicePath, -1);
else
this._applet.agent_reply_pincode(this._devicePath, null);
}
this.destroy();
}));
},
_canActivateOkButton: function() {
// PINs have a fixed length of 6
if (this._numeric)
return this._entry.clutter_text.text.length == 6;
if (nDevices > 0)
this._item.status.text = ngettext("%d Connected Device", "%d Connected Devices", nDevices).format(nDevices);
else
return true;
}
this._item.status.text = _("Not Connected");
},
});

View File

@ -11,9 +11,11 @@ const Slider = imports.ui.slider;
const BUS_NAME = 'org.gnome.SettingsDaemon.Power';
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
const BrightnessInterface = <interface name="org.gnome.SettingsDaemon.Power.Screen">
<property name='Brightness' type='i' access='readwrite'/>
</interface>;
const BrightnessInterface = '<node> \
<interface name="org.gnome.SettingsDaemon.Power.Screen"> \
<property name="Brightness" type="i" access="readwrite"/> \
</interface> \
</node>';
const BrightnessProxy = Gio.DBusProxy.makeProxyWrapper(BrightnessInterface);
@ -46,7 +48,7 @@ const Indicator = new Lang.Class({
this._item.actor.add(icon);
this._item.actor.add(this._slider.actor, { expand: true });
this._item.actor.connect('button-press-event', Lang.bind(this, function(actor, event) {
this._slider.startDragging(event);
return this._slider.startDragging(event);
}));
this._item.actor.connect('key-press-event', Lang.bind(this, function(actor, event) {
return this._slider.onKeyPressEvent(actor, event);

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