Compare commits

...

93 Commits
3.5.2 ... 3.5.3

Author SHA1 Message Date
3d5312e8d2 Bump version to 3.5.3
Update NEWS
Require Mutter 3.5.3
2012-06-26 00:03:48 +02:00
bc91b7dcae remoteSearch: Parse 'DesktopId' field in key file
We strongly expect applications to use the same values for the
'Title'/'Icon' fields in their search provider .ini file as the
'Name'/'Icon' fields in their .desktop file. Rather than requiring
applications to duplicate those fields, allow them to specify a
'DesktopId' field instead to point to the corresponding .desktop
file, which makes it possible to ship search provider files without
translatable strings (which is nice given that merging translations
into search provider files lacks a standard rule).

https://bugzilla.gnome.org/show_bug.cgi?id=678816
2012-06-25 22:57:31 +02:00
4d77eb94ff remoteSearch: Use GIcon instead of icon name
Currently we pass an icon name for the 'icon' parameter of the
RemoteSearchProvider constructor. In hindsight, using a GIcon
instead will give us a bit more flexibility, so change it.

https://bugzilla.gnome.org/show_bug.cgi?id=678816
2012-06-25 22:57:31 +02:00
1b8d03f945 St: reference the StTheme from StThemeNode
This is a partial revert of 7eaf231e56,
which caused segfaults. For some reason, StThemeNodes seem to survive
style changes.
2012-06-25 14:12:07 +02:00
de2dcfeb99 Update Punjabi Translation 2012-06-25 06:56:00 +05:30
c7196a519f tests: Run each test in a function
As the global object of a context is rooted, if we want the GC to act
on these objects we need to take them out of the globals.

https://bugzilla.gnome.org/show_bug.cgi?id=678737
2012-06-24 19:20:48 -04:00
0d82ce5210 tests: Don't use the default stage
https://bugzilla.gnome.org/show_bug.cgi?id=678737
2012-06-24 19:20:48 -04:00
3ce9ad05b3 run-js-test: GC twice after running a test
When running with Valgrind, this helps us ensure that we're managing
memory correctly. We need to GC twice as finalizing an object in the
sweep can unroot objects which were already marked. Technically, it
could be that we'll need to GC more than twice, but GCing twice should
hopefully last us for now.

https://bugzilla.gnome.org/show_bug.cgi?id=678737
2012-06-24 19:20:48 -04:00
ab75faac74 test-theme: Clean up after ourselves
https://bugzilla.gnome.org/show_bug.cgi?id=678737
2012-06-24 19:20:48 -04:00
9e25e13218 test-theme: Fix
St requires GTK+ to be initted.

https://bugzilla.gnome.org/show_bug.cgi?id=678737
2012-06-24 19:20:48 -04:00
69e1503c6d st-widget: Free the inline style
https://bugzilla.gnome.org/show_bug.cgi?id=678737
2012-06-24 19:20:47 -04:00
7eaf231e56 st-theme-node: Don't hold a ref to the theme context or the theme
This would just be a circular reference, which we need to avoid.

https://bugzilla.gnome.org/show_bug.cgi?id=678737
2012-06-24 19:20:47 -04:00
a347a72091 st-widget: Fix st_widget_set_theme
https://bugzilla.gnome.org/show_bug.cgi?id=678737
2012-06-24 19:20:47 -04:00
560daec913 st-widget: Chain up in the dispose handler for accessibility objects
https://bugzilla.gnome.org/show_bug.cgi?id=678737
2012-06-24 19:20:47 -04:00
d9e968d863 Updated Galician translations 2012-06-24 23:34:42 +02:00
556d5d181e st-widget: fix GList leak
clutter_actor_get_children returns a newly allocated GList and it was
not freed.

However, as there's no reason to copy the children list, switch to
iterator api.

https://bugzilla.gnome.org/show_bug.cgi?id=678406
2012-06-24 00:24:42 -04:00
4e4092f9e8 pokit-agent: fix memory leak
https://bugzilla.gnome.org/show_bug.cgi?id=678406
2012-06-24 00:24:42 -04:00
fd62aba71c Updated Greek translation 2012-06-23 01:30:41 +03:00
ef0aa65774 clock: Switch to using GnomeWallClock, delete clock GSettings controls
This avoids us having to poll once a second, among other things.  For
more information, see the linked bug chain.

See https://bugzilla.gnome.org/show_bug.cgi?id=657958 for the new
clock keys.

https://bugzilla.gnome.org/show_bug.cgi?id=657074
2012-06-22 18:10:46 -04:00
6b5f9a647a mount-operation: implement org.Gtk.MountOperationHandler
Use the ShellMountOperation dialogs we have to implement a DBus API
allowing other processes to display them.
Since GtkMountOperation now tries to call into our DBus implementation,
every application that uses a GtkMountOperation will gain integration
with our shell dialogs (but will still handle the actual communication
with GVfs).

https://bugzilla.gnome.org/show_bug.cgi?id=678516
2012-06-22 15:55:56 -04:00
01c07fbea1 userMenu: Rename 'Hidden' to 'Invisible'
Apparently only the Telepathy API calls this status 'Hidden',
Empathy and most network use 'Invisible' (also see
https://bugzilla.gnome.org/show_bug.cgi?id=603472).

https://bugzilla.gnome.org/show_bug.cgi?id=658955
2012-06-22 14:00:32 +02:00
81929c2a92 Updated Greek translation 2012-06-22 13:43:03 +03:00
d9ff8e3122 Updated Slovenian translation 2012-06-22 09:36:52 +02:00
0c4692ae58 appDisplay: Fix recursive directory NoDisplay testing
We were accidentally testing NoDisplay on the wrong directory.

https://bugzilla.gnome.org/show_bug.cgi?id=658176
2012-06-21 16:03:48 -04:00
df56ff4f09 app-system: Don't show items with NoDisplay parents in the search
Additionally, require gnome-menus 3.5.3, as we're using new API
introduced there.

https://bugzilla.gnome.org/show_bug.cgi?id=658176
2012-06-21 16:03:48 -04:00
96cdc9c4eb app-system: Use g_slist_free_full
https://bugzilla.gnome.org/show_bug.cgi?id=658176
2012-06-21 16:03:48 -04:00
3b4ad5cd7d app-system: Clean up imports
https://bugzilla.gnome.org/show_bug.cgi?id=658176
2012-06-21 16:03:48 -04:00
317c6b77f3 Util: update trySpawn to new gjs GError mapping
Error rewriting code used an old version of the gjs GError support,
and set a readonly .message property.

https://bugzilla.gnome.org/show_bug.cgi?id=678502
2012-06-21 18:28:26 +02:00
e5f5a2adaa Updated Spanish translation 2012-06-21 12:30:52 +02:00
594c3174ab mount-operation: fix indentation for some callbacks
Nothing to see here, move along.

https://bugzilla.gnome.org/show_bug.cgi?id=674962
2012-06-20 22:21:23 -04:00
5b7e4bb4a7 mount-operation: don't use global.get_current_time()
If we don't specify it, it will be picked up by default.

https://bugzilla.gnome.org/show_bug.cgi?id=674962
2012-06-20 22:21:23 -04:00
f7c0f826d4 autorun: use GError.matches() to fix some FIXMEs
Discard FAILED_HANDLED errors, now that we can detect them from JS.

https://bugzilla.gnome.org/show_bug.cgi?id=674962
2012-06-20 22:21:23 -04:00
66af7de6d6 automount: re-use the same dialog when passphrase doesn't match
Wait until the completion of the mount operation before dismissing the
passphrase dialog, so in case it fails, we can re-use the same dialog
with an error message (like e.g. PolicyKit auth dialogs) instead of
showing a brand new one.

https://bugzilla.gnome.org/show_bug.cgi?id=674962
2012-06-20 22:21:23 -04:00
d1815a36d0 mount-operation: turn the passphrase prompt into a dialog
Instead of a notification. This also adds space for a checkbox allowing
to remember the passphrase.

https://bugzilla.gnome.org/show_bug.cgi?id=674962
2012-06-20 22:21:23 -04:00
61de3de909 NetworkMenu: prefer wifi/3g over vpn in the panel
Wifi and mobile broadband have signal indicators and are thus
more useful than vpn icons in the panel. Therefore, in the case
we have both wifi/3g and VPN we prefer the former as the "primary
icon" and add a lock next to it.
Behavior when VPN is added to wired or other connections is still
preserved: the wired icon is replaced by vpn.

https://bugzilla.gnome.org/show_bug.cgi?id=672591
2012-06-20 17:23:53 -04:00
36888a34d6 NetworkMenu: sort wifi networks by strength
Sorting by strength is what the other OSes do by default, and it
provides a better UX (by offering your hotspot and router before
the one from your neighbor).

https://bugzilla.gnome.org/show_bug.cgi?id=658946
2012-06-20 23:05:57 +02:00
646435ee3e automount: don't allow autorun for things we mount at startup
We don't want to potentially flood the user with notifications about
mounts when starting up the system.

https://bugzilla.gnome.org/show_bug.cgi?id=660595
2012-06-20 10:15:37 -04:00
cbb8d5b0dc autorun: only obey allowAutorun flag for transient notifications
Only apply the allowAutorun flag for transient notifications, not for
mounts that end up in the resident notification well.

Also, stop looking at volume.can_automount() here, since we already
checked that previously in the mounter, and allowAutorun is enough.

https://bugzilla.gnome.org/show_bug.cgi?id=660595
2012-06-20 10:15:29 -04:00
ebee01b355 autorun: simplify code
Split autorun conditions into separate logic blocks instead of a single
huge if.

https://bugzilla.gnome.org/show_bug.cgi?id=660595
2012-06-20 10:15:22 -04:00
0e3795b2f3 autorun: change logic for ignoring volumes
Previously, a volume was being ignored from autorun if one of these two
conditions were met:
- its mount root file had a native scheme and was mounted in a
  non-hidden location
- it had a volume that could have been automounted, and had a flag set
  by the shell to allow autorun

In order to effectively ignore volumes that we don't mount ourselves
from our notification system, we have to meet both conditions at the
same time instead.

https://bugzilla.gnome.org/show_bug.cgi?id=660595
2012-06-20 10:15:16 -04:00
de73128d47 mount-operation: close the password entry on operation abort
If the device disappears in the middle of a mount operation, make sure
to dismiss the password prompt automatically.

https://bugzilla.gnome.org/show_bug.cgi?id=673787
2012-06-20 10:14:07 -04:00
281b0a3e63 mount-operation: fix exceptions when showing password entry
This is a fallout from some changes in MessageTray.Source, which now
requires either defining the iconName/iconType properties on it, or
implementing createNotificationIcon, and we're not doing any of those.
Fix it by storing the gicon of the source object and using a helper
method to create the icon actor on demand, to avoid any case when the
same actor might be added twice to different containers.

https://bugzilla.gnome.org/show_bug.cgi?id=678428
2012-06-20 10:07:50 -04:00
c414f9ac9d build: Fix the case of building without folks 2012-06-19 23:09:40 +02:00
77242cfec0 build: Make folks optional
The dependency chain spirals out from folks->zeitgeist->xapian...and
I'm really not interested in pulling in all of that into the core
shell.

There is work on splitting out contact search into gnome-contacts; I'd
add a bug link but Bugzilla is down.
2012-06-19 16:34:23 -04:00
8ebbba6eb8 Disable unredirection when a modal operation is active
When the shell takes control of the screen (for example to show
a modal dialog or to lock the screen), it must reestablish itself
on top of the stack, and in particular restore any unredirected
window so that it is composited below the Shell UI.

Reviewed-By: drago01 in IRC.
2012-06-19 19:56:06 +02:00
0804cefbee workspace: Minor cleanup
_hideAllOverlays() has only been used in a single place for quite
some time now, so move its code into the caller and save a loop.

https://bugzilla.gnome.org/show_bug.cgi?id=678416
2012-06-19 18:24:52 +02:00
2404d2935d workspace: Remove unused method
This one hasn't been used for quite a while now ...

https://bugzilla.gnome.org/show_bug.cgi?id=678416
2012-06-19 18:24:51 +02:00
e6f5e21b5d Enable the Screen Reader menu item
https://bugzilla.gnome.org/show_bug.cgi?id=663256
2012-06-19 12:21:05 -04:00
c303c6b5c1 shell-app: Update app menu if necessary
Currently we assume that GTK_UNIQUE_BUS_NAME is shared between all
windows of an application. This assumption does not hold true for
applications that specify G_APPLICATION_NON_UNIQUE, so make sure
to update the menu as necessary.

https://bugzilla.gnome.org/show_bug.cgi?id=676238
2012-06-19 17:57:34 +02:00
f58e8f2a35 Depend on gjs 1.33.2
Shell uses the 'format' module which has been introduced in gjs 1.33.2.

https://bugzilla.gnome.org/show_bug.cgi?id=678396
2012-06-19 14:17:59 +02:00
ededba0c6d telepathyClient: ignore invalidated channels
There is a race if a channel is invalidated during its preparation: the
'invalidated' signal is already emitted so the Shell will never notice.
We fix this by simply checking if the channel is already invalidated when
receiving it from telepathy-glib.

In the approving case, we reject the full ChannelDispatchOperation as we only
support approving one channel at the time.

https://bugzilla.gnome.org/show_bug.cgi?id=677457
2012-06-18 09:56:12 +02:00
e112fa92fe telepathyClient: decline dispatch op when approving unsupported channel type
It shouldn't happen in theory but best to be safe than sorry.

https://bugzilla.gnome.org/show_bug.cgi?id=677457
2012-06-18 09:56:12 +02:00
7524210d1f st-texture-cache: fix GtkIconInfo leak
https://bugzilla.gnome.org/show_bug.cgi?id=678079
2012-06-15 01:05:08 -04:00
201dc05416 ShellContactSystem: fix GSList of utf8 leak
https://bugzilla.gnome.org/show_bug.cgi?id=678079
2012-06-15 01:05:08 -04:00
2b34978993 ShellContactSystem: fix GSList leak
https://bugzilla.gnome.org/show_bug.cgi?id=678079
2012-06-15 01:05:08 -04:00
447246da74 shell-util: fix GFile leak
https://bugzilla.gnome.org/show_bug.cgi?id=678079
2012-06-15 01:05:08 -04:00
1cf2bb6646 ShellContactSystem: fix GeeMapIterator leak
https://bugzilla.gnome.org/show_bug.cgi?id=678079
2012-06-15 01:05:08 -04:00
de1eafb564 Always enable a11y
https://bugzilla.gnome.org/show_bug.cgi?id=678095
2012-06-14 18:24:57 +01:00
22f0099a5d main: NO_GAIL envvar is a noop
It's not used by GTK+ versions we require

https://bugzilla.gnome.org/show_bug.cgi?id=678096
2012-06-14 18:24:57 +01:00
300eb87016 Updated Arabic translation 2012-06-13 20:40:27 +02:00
aa38b16368 l10n: Updated Italian translation 2012-06-13 19:37:39 +02:00
266bfdf739 Updated POTFILES.in 2012-06-12 23:53:31 +02:00
19ef6b0421 shellDBus: Add a method to reload extensions
A common request from extension developers has been to reload their
extensions without restarting the Shell.

https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-12 17:16:09 -04:00
75570b7995 extensionSystem: Add a method to unload an extension
https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-12 17:16:09 -04:00
3290bfae68 extensionSystem: Remove the two-step initialize
Initially, extensions were loaded after they shell had fully created
the session and all objects, but this didn't allow extensions easy
ways to monkey patch prototypes, as most functions had already been
bound. Remove the historical vestigal function, and just merge the
two together.

https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-12 17:16:09 -04:00
0805d7a35f extensionSystem: Make the init function optional
A large amount of extensions have something like this in them:

    function init() {}

Since we have encouraged extension authors to try and not make any
changes in init, it feels weird and strange to have to create an
initialization function that does nothing. From now on, don't require
it.

https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-12 17:16:09 -04:00
11278a0814 extensionSystem: Load/unload stylesheets on enable/disable
I'm not entirely sure why this wasn't caught earlier.

https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-12 17:16:08 -04:00
86de6f5861 extensionUtils: Create and load the extension object when scanning
This reduces some duplicate code when loading extensions.

https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-12 17:16:08 -04:00
498b023989 extensionUtils: Use signals rather than callbacks for finding extensions
This allows us to move to a file-monitor based approach in the future.

Since we need signals, we convert the current set of functions to an
object we attach signals too, leading to the new ExtensionFinder object.

https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-12 17:16:08 -04:00
5265884af9 extensionUtils: Remove userExtensionsDir
Make this less stateful

https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-12 17:16:08 -04:00
cdbe0bbf38 extensionUtils: Remove version check for js-version
This is seldomly used, and isn't checked in SweetTooth. Just remove
this inconsistency here rather than adding infrastructure to manage
and check it elsewhere.

https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-12 17:16:08 -04:00
feef35a8ca extensionUtils: Don't write to the filesystem on start
Create the potentially empty directory when we need to, not when we
don't.

https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-12 17:16:08 -04:00
3ff51da529 extensionSystem: Split off the extension downloader into a separate file
This keeps all UI out of the extension system, leaving it strictly for
loading and unloading extensions.

https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-12 17:16:08 -04:00
496e9f7b16 st: Trigger theme updates on resolution changes
Commit de8a66d4ce removed our own DPI handling for the one found
it Clutter, but broke resolution updates at runtime (for instance
when setting the "Large Text" option in Universal Access).

https://bugzilla.gnome.org/show_bug.cgi?id=677975
2012-06-12 22:40:59 +02:00
0b30dc29a5 Updated Lithuanian translation 2012-06-12 12:19:33 +03:00
15b4d29e70 Updated Norwegian bokmål translation 2012-06-11 22:04:54 +02:00
9eee4b7687 Fix name for the schemas file 2012-06-11 22:04:51 +02:00
196f6c241a appDisplay: Don't show apps in NoDisplay categories in the All view
We explicitly include NoDisplay applications in the ShellAppSystem because
we want app tracking for them, but we explicitly filter NoDisplay applications
out when showing them to the user because we don't want to show them to the
user. We also based our "All" apps view on a flattened list of apps. While
we did check for NoDisplay on the app item itself, we didn't check against
its parents. Refactor the app display view to not use a separate flat list
of applications, but instead a concatenation of all the applications in all
the loaded categories.

https://bugzilla.gnome.org/show_bug.cgi?id=658176
2012-06-11 13:55:59 -04:00
c9296191a8 js: Remove unnecessary versions of clutter_actor_get_children
clutter_actor_get_children requires making a temporary GSList from
a linked list structure, and then creating a JS Array from that GSList.
For simple cases like the number of children, use clutter_actor_get_n_children.

https://bugzilla.gnome.org/show_bug.cgi?id=677426
2012-06-11 13:10:25 -04:00
c7b4022283 extensionSystem: Initialize the HTTP session after parse time
We really shouldn't be creating GObjects while we're still parsing other
files for performance and clarity reasons.

https://bugzilla.gnome.org/show_bug.cgi?id=677586
2012-06-11 13:07:40 -04:00
a7ecc4cdd6 Revert "extensionSystem: Initialize the HTTP session after parse time"
This reverts commit df5298d59c.
2012-06-11 13:06:14 -04:00
df5298d59c extensionSystem: Initialize the HTTP session after parse time
We really shouldn't be creating GObjects while we're still parsing other
files for performance and clarity reasons.
2012-06-11 13:04:56 -04:00
b79d17332e data: Actually substitute @GETTEXT_PACKAGE@ in schema files
https://bugzilla.gnome.org/show_bug.cgi?id=677497
2012-06-11 13:00:33 -04:00
675572a41a Updated Greek translation 2012-06-11 18:31:33 +03:00
0078fe9349 Updated Russian translation 2012-06-11 10:38:29 +04:00
dc004f6eb7 modalDialog: show dialogs on monitor with the mouse pointer
Show the dialog on the monitor containing the pointer, rather than
the monitor with active focused window. This brings it inline with
the behaviour seen when launching applications.

Remove the focusMonitor/focusIndex from LayoutManager. These
properties were only used by the modal dialogs. Remove them since
they are not being used elsewhere.

https://bugzilla.gnome.org/show_bug.cgi?id=642591
2012-06-08 23:05:56 -04:00
07f1a05ab4 Updated Slovenian translation 2012-06-08 21:29:52 +02:00
54292a99af calendar-server: Remove left-over include
GConf-free at last! GConf-free at last!
2012-06-08 19:08:28 +02:00
f5933c8cb8 messageTray: Don't show non urgent notifications while in fullscreen
Don't show non urgent notifications when the primary monitor
is in fullscreen (user watching a movie, having a presentation,
playing a game ...).

Once the user leaves fullscreen show the messagetray so that the user don't
miss any notification (same as in "back from idle").

https://bugzilla.gnome.org/show_bug.cgi?id=677590
2012-06-07 20:36:58 +02:00
6700f86145 LayoutManager: Add primary-fullscreen-changed signal
Add a signal to the LayoutManager which gets emitted once the primary monitor
enters or leaves the fullscreen state.

https://bugzilla.gnome.org/show_bug.cgi?id=677590
2012-06-07 20:36:48 +02:00
b31d22488e calendar: Adapt to Evolution-Data-Server API changes.
Adapt the calendar-server to some major API changes in E-D-S 3.5.3.

More details about the breakage:
http://mbarnes.livejournal.com/4631.html

https://bugzilla.gnome.org/show_bug.cgi?id=677402
2012-06-07 12:50:46 -04:00
6382eeb8fd network: Remove old API
The underscored properties were removed a while ago.

https://bugzilla.gnome.org/show_bug.cgi?id=677515
2012-06-05 18:10:25 -04:00
b07345a55c main: Adapt to new plugin API 2012-06-05 16:33:14 -04:00
86 changed files with 4955 additions and 4613 deletions

43
NEWS
View File

@ -1,3 +1,46 @@
3.5.3
=====
* calendar: Adapt to Evolution-Data-Server API changes [Matthew; #677402]
* messageTray: Don't show non urgent notifications while in fullscreen
[Adel; #677590]
* modalDialog: show dialogs on monitor with the mouse pointer [Tim; #642591]
* extensionSystem: Prepare for extension updating system [Jasper; #677586]
* appDisplay: Don't show apps in NoDisplay categories in the All view
[Jasper; #658176]
* st: Trigger theme updates on resolution changes [Florian; #677975]
* Always enable a11y [Bastien; #678095]
* telepathyClient: ignore invalidated channels [Guillaume; #677457]
* shell-app: Update app menu if necessary [Florian; #676238]
* Enable the Screen Reader menu item [Matthias; #663256]
* Disable unredirection when a modal operation is active [Giovanni]
* Make folks optional [Colin]
* Improve mount-operation support [Cosimo]
- Fix exception when showing password entry [#678428]
- Close the password entry on operation abort [#673787]
- autorun: Don't allow autorun for things we mount on startup [#660595]
- Turn passphrase prompt into a dialog [#674962]
- Implement org.Gtk.MountOperationHandler [#678516]
* Network menu improvements
- Sort Wifi networks by strength [Giovanni; #658946]
- Prefer wifi/3g over VPN in the panel [Cosimo; #672591]
* clock: Switch to using GnomeWallClock [Colin; #657074]
* remoteSearch: Allow to reference .desktop file for Title/Icon
[Florian; #678816]
* Fix memory leaks [Jasper, Pavel; #678079, #678406, #678737]
* Misc fixes [Florian, Giovanni, Guillaume, Jasper, Kjartan, Piotr, Rui;
#658955, #677497, #678396, #678502]
* Misc cleanups [Bastien, Florian, Jasper; #677426, #677515, #678096, #678416]
Contributors:
Matthew Barnes, Giovanni Campagna, Cosimo Cecchi, Matthias Clasen,
Guillaume Desmottes, Piotr Drąg, Adel Gadllah, Tim L, Kjartan Maraas,
Rui Matos, Florian Müllner, Bastien Nocera, Jasper St. Pierre, Colin Walters
Translations:
Matej Urbančič [sl], Yuri Kozlov [ru], Tom Tryfonidis [el],
Kjartan Maraas [nb], Žygimantas Beručka [lt], Luca Ferretti [it],
Khaled Hosny [ar], Daniel Mustieles [es], Fran Diéguez [gl], A S Alam [pa]
3.5.2
=====
* main: Move 'toggle-recording' binding into the shell [Florian; #674377]

View File

@ -1,5 +1,5 @@
AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.5.2],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_INIT([gnome-shell],[3.5.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([src/shell-global.c])
@ -62,29 +62,47 @@ AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
CLUTTER_MIN_VERSION=1.9.16
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=1.29.18
MUTTER_MIN_VERSION=3.5.2
GJS_MIN_VERSION=1.33.2
MUTTER_MIN_VERSION=3.5.3
FOLKS_MIN_VERSION=0.5.2
GTK_MIN_VERSION=3.3.9
GIO_MIN_VERSION=2.31.6
LIBECAL_MIN_VERSION=2.32.0
LIBEDATASERVER_MIN_VERSION=1.2.0
LIBEDATASERVERUI_MIN_VERSION=2.91.6
LIBECAL_MIN_VERSION=3.5.3
LIBEDATASERVER_MIN_VERSION=3.5.3
LIBEDATASERVERUI_MIN_VERSION=3.5.3
TELEPATHY_GLIB_MIN_VERSION=0.17.5
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
POLKIT_MIN_VERSION=0.100
STARTUP_NOTIFICATION_MIN_VERSION=0.11
GCR_MIN_VERSION=3.3.90
GNOME_DESKTOP_REQUIRED_VERSION=3.5.1
GNOME_MENUS_REQUIRED_VERSION=3.5.3
AC_ARG_WITH(folks,
AS_HELP_STRING([--with-folks],
[Enable folks support]),
[with_folks=$withval], [with_folks=yes])
if test x${with_folks} = xyes; then
FOLKS_REQUIREMENT="folks >= $FOLKS_MIN_VERSION"
AC_DEFINE([HAVE_FOLKS], [1], [folks support])
AC_SUBST([HAVE_FOLKS],[1])
else
FOLKS_REQUIREMENT=
AC_SUBST([HAVE_FOLKS],[0])
fi
AM_CONDITIONAL(BUILD_WITH_FOLKS, test x${with_folks} = xyes)
# Collect more than 20 libraries for a prize!
PKG_CHECK_MODULES(GNOME_SHELL, gio-unix-2.0 >= $GIO_MIN_VERSION
libxml-2.0
gtk+-3.0 >= $GTK_MIN_VERSION
folks >= $FOLKS_MIN_VERSION
atk-bridge-2.0
$FOLKS_REQUIREMENT
libmutter >= $MUTTER_MIN_VERSION
gjs-internals-1.0 >= $GJS_MIN_VERSION
libgnome-menu-3.0 $recorder_modules
libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
$recorder_modules
gdk-x11-3.0 libsoup-2.4
gl
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
@ -105,10 +123,6 @@ PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
GJS_VERSION=`$PKG_CONFIG --modversion gjs-internals-1.0`
AC_DEFINE_UNQUOTED([GJS_VERSION], ["$GJS_VERSION"], [The version of GJS we're linking to])
AC_SUBST([GJS_VERSION], ["$GJS_VERSION"])
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
saved_CFLAGS=$CFLAGS

View File

@ -53,9 +53,14 @@ dist_theme_DATA = \
theme/ws-switch-arrow-up.svg \
theme/ws-switch-arrow-down.svg
gsettings_SCHEMAS = org.gnome.shell.gschema.xml org.gnome.shell.evolution.calendar.gschema.xml
gsettings_SCHEMAS = org.gnome.shell.gschema.xml
@INTLTOOL_XML_NOMERGE_RULE@
%.gschema.xml.in: %.gschema.xml.in.in Makefile
$(AM_V_GEN) sed -e 's|@GETTEXT_PACKAGE[@]|$(GETTEXT_PACKAGE)|g' \
$< > $@ || rm $@
@GSETTINGS_RULES@
# We need to compile schemas at make time
@ -80,13 +85,13 @@ EXTRA_DIST = \
$(menu_DATA) \
$(shaders_DATA) \
$(convert_DATA) \
org.gnome.shell.evolution.calendar.gschema.xml.in \
org.gnome.shell.gschema.xml.in
org.gnome.shell.gschema.xml.in.in
CLEANFILES = \
gnome-shell.desktop.in \
gnome-shell-extension-prefs.in \
$(desktop_DATA) \
$(gsettings_SCHEMAS) \
gschemas.compiled
gschemas.compiled \
org.gnome.shell.gschema.valid \
org.gnome.shell.gschema.xml.in

View File

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

View File

@ -61,7 +61,6 @@ value here is from the TpConnectionPresenceType enumeration.</_summary>
<_summary>Internally used to store the last session presence status for the user. The
value here is from the GsmPresenceStatus enumeration.</_summary>
</key>
<child name="clock" schema="org.gnome.shell.clock"/>
<child name="calendar" schema="org.gnome.shell.calendar"/>
<child name="recorder" schema="org.gnome.shell.recorder"/>
<child name="keybindings" schema="org.gnome.shell.keybindings"/>
@ -108,24 +107,6 @@ value here is from the GsmPresenceStatus enumeration.</_summary>
</key>
</schema>
<schema id="org.gnome.shell.clock" path="/org/gnome/shell/clock/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="show-seconds" type="b">
<default>false</default>
<_summary>Show time with seconds</_summary>
<_description>
If true, display seconds in time.
</_description>
</key>
<key name="show-date" type="b">
<default>false</default>
<_summary>Show date in clock</_summary>
<_description>
If true, display date in the clock, in addition to time.
</_description>
</key>
</schema>
<schema id="org.gnome.shell.recorder" path="/org/gnome/shell/recorder/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="framerate" type="i">

View File

@ -436,6 +436,10 @@ StScrollBar StButton#vhandle:hover
-boxpointer-gap: 4px
}
#networkMenu {
spacing: 4px;
}
/* User Menu */
#panelUserMenu {

View File

@ -6,9 +6,8 @@ misc/config.js: misc/config.js.in Makefile
[ -d $(@D) ] || $(mkdir_p) $(@D) ; \
sed -e "s|[@]PACKAGE_NAME@|$(PACKAGE_NAME)|g" \
-e "s|[@]PACKAGE_VERSION@|$(PACKAGE_VERSION)|g" \
-e "s|[@]GJS_VERSION@|$(GJS_VERSION)|g" \
-e "s|[@]HAVE_FOLKS@|$(HAVE_FOLKS)|g" \
-e "s|[@]HAVE_BLUETOOTH@|$(HAVE_BLUETOOTH)|g" \
-e "s|[@]SHELL_SYSTEM_CA_FILE@|$(SHELL_SYSTEM_CA_FILE)|g" \
-e "s|[@]GETTEXT_PACKAGE@|$(GETTEXT_PACKAGE)|g" \
-e "s|[@]datadir@|$(datadir)|g" \
-e "s|[@]libexecdir@|$(libexecdir)|g" \
@ -52,6 +51,7 @@ nobase_dist_js_DATA = \
ui/endSessionDialog.js \
ui/environment.js \
ui/extensionSystem.js \
ui/extensionDownloader.js \
ui/flashspot.js \
ui/iconGrid.js \
ui/keyboard.js \

View File

@ -202,24 +202,18 @@ const Application = new Lang.Class({
},
_scanExtensions: function() {
ExtensionUtils.scanExtensions(Lang.bind(this, function(uuid, dir, type) {
if (ExtensionUtils.extensions[uuid] !== undefined)
return;
let extension;
try {
extension = ExtensionUtils.createExtensionObject(uuid, dir, type);
} catch(e) {
logError(e, 'Could not create extensions object');
return;
}
let iter = this._model.append();
this._model.set(iter, [0, 1], [uuid, extension.metadata.name]);
this._extensionIters[uuid] = iter;
}));
let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', Lang.bind(this, this._extensionFound));
finder.scanExtensions();
},
_extensionFound: function(signals, extension) {
let iter = this._model.append();
this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]);
this._extensionIters[uuid] = iter;
},
_onActivate: function() {
this._window.present();
},
@ -268,7 +262,6 @@ function initEnvironment() {
function main(argv) {
initEnvironment();
ExtensionUtils.init();
Gettext.bindtextdomain(Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
Gettext.textdomain(Config.GETTEXT_PACKAGE);

View File

@ -4,10 +4,10 @@
const PACKAGE_NAME = '@PACKAGE_NAME@';
/* The version of this package */
const PACKAGE_VERSION = '@PACKAGE_VERSION@';
/* The version of GJS we're linking to */
const GJS_VERSION = '@GJS_VERSION@';
/* 1 if gnome-bluetooth is available, 0 otherwise */
const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;
/* 1 if folks is available, 0 otherwise */
const HAVE_FOLKS = @HAVE_FOLKS@;
/* gettext package */
const GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@';
/* locale dir */

View File

@ -3,6 +3,9 @@
// Common utils for the extension system and the extension
// preferences tool
const Lang = imports.lang;
const Signals = imports.signals;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const ShellJS = imports.gi.ShellJS;
@ -14,9 +17,6 @@ const ExtensionType = {
PER_USER: 2
};
// GFile for user extensions
var userExtensionsDir = null;
// Maps uuid -> metadata object
const extensions = {};
@ -88,9 +88,6 @@ function isOutOfDate(extension) {
if (!versionCheck(extension.metadata['shell-version'], Config.PACKAGE_VERSION))
return true;
if (extension.metadata['js-version'] && !versionCheck(extension.metadata['js-version'], Config.GJS_VERSION))
return true;
return false;
}
@ -155,45 +152,48 @@ function installImporter(extension) {
_extension = null;
}
function init() {
let userExtensionsPath = GLib.build_filenamev([global.userdatadir, 'extensions']);
userExtensionsDir = Gio.file_new_for_path(userExtensionsPath);
try {
if (!userExtensionsDir.query_exists(null))
userExtensionsDir.make_directory_with_parents(null);
} catch (e) {
logError(e, 'Could not create extensions directory');
}
}
const ExtensionFinder = new Lang.Class({
Name: 'ExtensionFinder',
function scanExtensionsInDirectory(callback, dir, type) {
let fileEnum;
let file, info;
try {
fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
} catch(e) {
logError(e, 'Could not enumerate extensions directory');
return;
}
_scanExtensionsInDirectory: function(dir, type) {
let fileEnum;
let file, info;
try {
fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
} catch(e) {
logError(e, 'Could not enumerate extensions directory');
return;
}
while ((info = fileEnum.next_file(null)) != null) {
let fileType = info.get_file_type();
if (fileType != Gio.FileType.DIRECTORY)
continue;
let uuid = info.get_name();
let extensionDir = dir.get_child(uuid);
callback(uuid, extensionDir, type);
}
fileEnum.close(null);
}
while ((info = fileEnum.next_file(null)) != null) {
let fileType = info.get_file_type();
if (fileType != Gio.FileType.DIRECTORY)
continue;
let uuid = info.get_name();
let extensionDir = dir.get_child(uuid);
function scanExtensions(callback) {
let systemDataDirs = GLib.get_system_data_dirs();
scanExtensionsInDirectory(callback, userExtensionsDir, ExtensionType.PER_USER);
for (let i = 0; i < systemDataDirs.length; i++) {
let dirPath = GLib.build_filenamev([systemDataDirs[i], 'gnome-shell', 'extensions']);
let dir = Gio.file_new_for_path(dirPath);
if (dir.query_exists(null))
scanExtensionsInDirectory(callback, dir, ExtensionType.SYSTEM);
let existing = extensions[uuid];
if (existing) {
log('Extension %s already installed in %s. %s will not be loaded'.format(uuid, existing.path, extensionDir.get_path()));
continue;
}
let extension = createExtensionObject(uuid, extensionDir, type);
this.emit('extension-found', extension);
}
fileEnum.close(null);
},
scanExtensions: function() {
let userExtensionsDir = Gio.File.new_for_path(GLib.build_filenamev([global.userdatadir, 'extensions']));
this._scanExtensionsInDirectory(userExtensionsDir, ExtensionType.PER_USER);
let systemDataDirs = GLib.get_system_data_dirs();
for (let i = 0; i < systemDataDirs.length; i++) {
let dirPath = GLib.build_filenamev([systemDataDirs[i], 'gnome-shell', 'extensions']);
let dir = Gio.file_new_for_path(dirPath);
if (dir.query_exists(null))
this._scanExtensionsInDirectory(dir, ExtensionType.SYSTEM);
}
}
}
});
Signals.addSignalMethods(ExtensionFinder.prototype);

View File

@ -89,18 +89,22 @@ function trySpawn(argv)
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
null, null);
} catch (err) {
if (err.code == GLib.SpawnError.G_SPAWN_ERROR_NOENT) {
err.message = _("Command not found");
} else {
/* Rewrite the error in case of ENOENT */
if (err.matches(GLib.SpawnError, GLib.SpawnError.NOENT)) {
throw new GLib.SpawnError({ code: GLib.SpawnError.NOENT,
message: _("Command not found") });
} else if (err instanceof GLib.Error) {
// The exception from gjs contains an error string like:
// Error invoking GLib.spawn_command_line_async: Failed to
// execute child process "foo" (No such file or directory)
// We are only interested in the part in the parentheses. (And
// we can't pattern match the text, since it gets localized.)
err.message = err.message.replace(/.*\((.+)\)/, '$1');
let message = err.message.replace(/.*\((.+)\)/, '$1');
throw new (err.constructor)({ code: err.code,
message: message });
} else {
throw err;
}
throw err;
}
// Dummy child watch; we don't want to double-fork internally
// because then we lose the parent-child relationship, which

View File

@ -22,6 +22,7 @@ const Search = imports.ui.search;
const Tweener = imports.ui.tweener;
const Workspace = imports.ui.workspace;
const Params = imports.misc.params;
const Util = imports.misc.util;
const MAX_APPLICATION_WORK_MILLIS = 75;
const MENU_POPUP_TIMEOUT = 600;
@ -36,6 +37,7 @@ const AlphabeticalView = new Lang.Class({
this._pendingAppLaterId = 0;
this._appIcons = {}; // desktop file id
this._allApps = [];
let box = new St.BoxLayout({ vertical: true });
box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
@ -60,16 +62,22 @@ const AlphabeticalView = new Lang.Class({
}));
},
_removeAll: function() {
removeAll: function() {
this._grid.removeAll();
this._appIcons = {};
this._allApps = [];
},
_addApp: function(app) {
addApp: function(app) {
var id = app.get_id();
let appIcon = new AppWellIcon(app);
if (this._appIcons[id] !== undefined)
return;
this._grid.addItem(appIcon.actor);
let appIcon = new AppWellIcon(app);
let pos = Util.insertSorted(this._allApps, app, function(a, b) {
return a.compare_by_name(b);
});
this._grid.addItem(appIcon.actor, pos);
appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
this._appIcons[id] = appIcon;
@ -120,14 +128,6 @@ const AlphabeticalView = new Lang.Class({
icon.actor.visible = true;
}
}
},
setAppList: function(apps) {
this._removeAll();
for (var i = 0; i < apps.length; i++) {
var app = apps[i];
this._addApp(app);
}
}
});
@ -147,7 +147,6 @@ const ViewByCategories = new Lang.Class({
// (used only before the actor is mapped the first time)
this._currentCategory = -2;
this._categories = [];
this._apps = null;
this._categoryBox = new St.BoxLayout({ vertical: true,
reactive: true,
@ -204,16 +203,19 @@ const ViewByCategories = new Lang.Class({
if (nextType == GMenu.TreeItemType.ENTRY) {
var entry = iter.get_entry();
var app = this._appSystem.lookup_app_by_tree_entry(entry);
if (!entry.get_app_info().get_nodisplay())
if (!entry.get_app_info().get_nodisplay()) {
this._view.addApp(app);
appList.push(app);
}
} else if (nextType == GMenu.TreeItemType.DIRECTORY) {
if (!dir.get_is_nodisplay())
this._loadCategory(iter.get_directory(), appList);
var itemDir = iter.get_directory();
if (!itemDir.get_is_nodisplay())
this._loadCategory(itemDir, appList);
}
}
},
_addCategory: function(name, index, dir, allApps) {
_addCategory: function(name, index, dir) {
let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
style_class: 'app-filter',
x_align: St.Align.START,
@ -225,7 +227,6 @@ const ViewByCategories = new Lang.Class({
var apps;
if (dir == null) {
apps = allApps;
this._allCategoryButton = button;
} else {
apps = [];
@ -239,6 +240,7 @@ const ViewByCategories = new Lang.Class({
},
_removeAll: function() {
this._view.removeAll();
this._categories = [];
this._categoryBox.destroy_all_children();
},
@ -246,13 +248,8 @@ const ViewByCategories = new Lang.Class({
refresh: function() {
this._removeAll();
var allApps = Shell.AppSystem.get_default().get_all();
allApps.sort(function(a, b) {
return a.compare_by_name(b);
});
/* Translators: Filter to display all applications */
this._addCategory(_("All"), -1, null, allApps);
this._addCategory(_("All"), -1, null);
var tree = this._appSystem.get_tree();
var root = tree.get_root_directory();
@ -270,7 +267,6 @@ const ViewByCategories = new Lang.Class({
}
}
this._view.setAppList(allApps);
this._selectCategory(-1);
if (this._focusDummy) {

View File

@ -123,7 +123,8 @@ const AutomountManager = new Lang.Class({
let volumes = this._volumeMonitor.get_volumes();
volumes.forEach(Lang.bind(this, function(volume) {
this._checkAndMountVolume(volume, { checkSession: false,
useMountOp: false });
useMountOp: false,
allowAutorun: false });
}));
return false;
@ -201,7 +202,8 @@ const AutomountManager = new Lang.Class({
_checkAndMountVolume: function(volume, params) {
params = Params.parse(params, { checkSession: true,
useMountOp: true });
useMountOp: true,
allowAutorun: true });
if (params.checkSession) {
// if we're not in the current ConsoleKit session,
@ -236,15 +238,20 @@ const AutomountManager = new Lang.Class({
if (params.useMountOp) {
let operation = new ShellMountOperation.ShellMountOperation(volume);
this._mountVolume(volume, operation.mountOp);
this._mountVolume(volume, operation, params.allowAutorun);
} else {
this._mountVolume(volume, null);
this._mountVolume(volume, null, params.allowAutorun);
}
},
_mountVolume: function(volume, operation) {
this._allowAutorun(volume);
volume.mount(0, operation, null,
_mountVolume: function(volume, operation, allowAutorun) {
if (allowAutorun)
this._allowAutorun(volume);
let mountOp = operation ? operation.mountOp : null;
volume._operation = operation;
volume.mount(0, mountOp, null,
Lang.bind(this, this._onVolumeMounted));
},
@ -253,15 +260,19 @@ const AutomountManager = new Lang.Class({
try {
volume.mount_finish(res);
this._closeOperation(volume);
} catch (e) {
let string = e.toString();
// FIXME: needs proper error code handling instead of this
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
if (string.indexOf('No key available with this passphrase') != -1)
// FIXME: we will always get G_IO_ERROR_FAILED from the gvfs udisks
// backend in this case, see
// https://bugs.freedesktop.org/show_bug.cgi?id=51271
if (e.message.indexOf('No key available with this passphrase') != -1) {
this._reaskPassword(volume);
else
log('Unable to mount volume ' + volume.get_name() + ': ' + string);
} else {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
log('Unable to mount volume ' + volume.get_name() + ': ' + e.toString());
this._closeOperation(volume);
}
}
},
@ -273,8 +284,16 @@ const AutomountManager = new Lang.Class({
},
_reaskPassword: function(volume) {
let operation = new ShellMountOperation.ShellMountOperation(volume, { reaskPassword: true });
this._mountVolume(volume, operation.mountOp);
let existingDialog = volume._operation ? volume._operation.borrowDialog() : null;
let operation =
new ShellMountOperation.ShellMountOperation(volume,
{ existingDialog: existingDialog });
this._mountVolume(volume, operation);
},
_closeOperation: function(volume) {
if (volume._operation)
volume._operation.close();
},
_allowAutorun: function(volume) {

View File

@ -23,12 +23,14 @@ const AutorunSetting = {
};
// misc utils
function ignoreAutorunForMount(mount) {
function shouldAutorunMount(mount, forTransient) {
let root = mount.get_root();
let volume = mount.get_volume();
if ((root.is_native() && !isMountRootHidden(root)) ||
(volume && volume.allowAutorun && volume.should_automount()))
if (!volume || (!volume.allowAutorun && forTransient))
return false;
if (!root.is_native() || isMountRootHidden(root))
return false;
return true;
@ -224,11 +226,9 @@ const AutorunManager = new Lang.Class({
try {
mount.unmount_with_operation_finish(res);
} catch (e) {
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
// but we can't access the error code from JS.
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
log('Unable to eject the mount ' + mount.get_name()
+ ': ' + e.toString());
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
log('Unable to eject the mount ' + mount.get_name()
+ ': ' + e.toString());
}
},
@ -236,11 +236,9 @@ const AutorunManager = new Lang.Class({
try {
source.eject_with_operation_finish(res);
} catch (e) {
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
// but we can't access the error code from JS.
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
log('Unable to eject the drive ' + source.get_name()
+ ': ' + e.toString());
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
log('Unable to eject the drive ' + source.get_name()
+ ': ' + e.toString());
}
},
@ -248,11 +246,9 @@ const AutorunManager = new Lang.Class({
try {
drive.stop_finish(res);
} catch (e) {
// FIXME: we need to ignore G_IO_ERROR_FAILED_HANDLED errors here
// but we can't access the error code from JS.
// See https://bugzilla.gnome.org/show_bug.cgi?id=591480
log('Unable to stop the drive ' + drive.get_name()
+ ': ' + e.toString());
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.FAILED_HANDLED))
log('Unable to stop the drive ' + drive.get_name()
+ ': ' + e.toString());
}
},
});
@ -270,7 +266,7 @@ const AutorunResidentSource = new Lang.Class({
},
addMount: function(mount, apps) {
if (ignoreAutorunForMount(mount))
if (!shouldAutorunMount(mount, false))
return;
let filtered = this._mounts.filter(function (element) {
@ -448,7 +444,7 @@ const AutorunTransientDispatcher = new Lang.Class({
return;
// if the mount doesn't want to be autorun, return
if (ignoreAutorunForMount(mount))
if (!shouldAutorunMount(mount, true))
return;
let setting = this._getAutorunSettingForType(contentTypes[0]);

View File

@ -448,7 +448,7 @@ const Calendar = new Lang.Class({
}
// All the children after this are days, and get removed when we update the calendar
this._firstDayIndex = this.actor.get_children().length;
this._firstDayIndex = this.actor.get_n_children();
},
_onStyleChange: function(actor, event) {

View File

@ -2,6 +2,7 @@
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const GnomeDesktop = imports.gi.GnomeDesktop;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Cairo = imports.cairo;
@ -16,14 +17,6 @@ const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const Calendar = imports.ui.calendar;
const UPowerGlib = imports.gi.UPowerGlib;
// in org.gnome.desktop.interface
const CLOCK_FORMAT_KEY = 'clock-format';
// in org.gnome.shell.clock
const CLOCK_SHOW_DATE_KEY = 'show-date';
const CLOCK_SHOW_SECONDS_KEY = 'show-seconds';
function _onVertSepRepaint (area)
{
@ -60,8 +53,8 @@ const DateMenuButton = new Lang.Class({
// role ATK_ROLE_MENU like other elements of the panel.
this.actor.accessible_role = Atk.Role.LABEL;
this._clock = new St.Label();
this.actor.add_actor(this._clock);
this._clockDisplay = new St.Label();
this.actor.add_actor(this._clockDisplay);
hbox = new St.BoxLayout({name: 'calendarArea' });
this.menu.addActor(hbox);
@ -73,7 +66,7 @@ const DateMenuButton = new Lang.Class({
// Date
this._date = new St.Label();
this.actor.label_actor = this._clock;
this.actor.label_actor = this._clockDisplay;
this._date.style_class = 'datemenu-date-label';
vbox.add(this._date);
@ -155,68 +148,13 @@ const DateMenuButton = new Lang.Class({
// Done with hbox for calendar and event list
// Track changes to clock settings
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
this._clockSettings = new Gio.Settings({ schema: 'org.gnome.shell.clock' });
this._desktopSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
this._clockSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
// https://bugzilla.gnome.org/show_bug.cgi?id=655129
this._upClient = new UPowerGlib.Client();
this._upClient.connect('notify-resume', Lang.bind(this, this._updateClockAndDate));
// Start the clock
this._clock = new GnomeDesktop.WallClock();
this._clock.connect('notify::clock', Lang.bind(this, this._updateClockAndDate));
this._updateClockAndDate();
},
_updateClockAndDate: function() {
let format = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);
let showDate = this._clockSettings.get_boolean(CLOCK_SHOW_DATE_KEY);
let showSeconds = this._clockSettings.get_boolean(CLOCK_SHOW_SECONDS_KEY);
let clockFormat;
let dateFormat;
switch (format) {
case '24h':
if (showDate)
/* Translators: This is the time format with date used
in 24-hour mode. */
clockFormat = showSeconds ? _("%a %b %e, %R:%S")
: _("%a %b %e, %R");
else
/* Translators: This is the time format without date used
in 24-hour mode. */
clockFormat = showSeconds ? _("%a %R:%S")
: _("%a %R");
break;
case '12h':
default:
if (showDate)
/* Translators: This is a time format with date used
for AM/PM. */
clockFormat = showSeconds ? _("%a %b %e, %l:%M:%S %p")
: _("%a %b %e, %l:%M %p");
else
/* Translators: This is a time format without date used
for AM/PM. */
clockFormat = showSeconds ? _("%a %l:%M:%S %p")
: _("%a %l:%M %p");
break;
}
let displayDate = new Date();
this._clock.set_text(displayDate.toLocaleFormat(clockFormat));
/* Translators: This is the date format to use when the calendar popup is
* shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
*/
dateFormat = _("%A %B %e, %Y");
this._date.set_text(displayDate.toLocaleFormat(dateFormat));
Mainloop.timeout_add_seconds(1, Lang.bind(this, this._updateClockAndDate));
return false;
this._clockDisplay.set_text(this._clock.clock);
},
_onOpenCalendarActivate: function() {

View File

@ -31,7 +31,6 @@ const St = imports.gi.St;
const Shell = imports.gi.Shell;
const GnomeSession = imports.misc.gnomeSession;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const Tweener = imports.ui.tweener;
@ -288,13 +287,13 @@ const EndSessionDialog = new Lang.Class({
this._applicationList.connect('actor-added',
Lang.bind(this, function() {
if (this._applicationList.get_children().length == 1)
if (this._applicationList.get_n_children() == 1)
scrollView.show();
}));
this._applicationList.connect('actor-removed',
Lang.bind(this, function() {
if (this._applicationList.get_children().length == 0)
if (this._applicationList.get_n_children() == 0)
scrollView.hide();
}));

View File

@ -0,0 +1,170 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Soup = imports.gi.Soup;
const St = imports.gi.St;
const Config = imports.misc.config;
const ExtensionUtils = imports.misc.extensionUtils;
const ExtensionSystem = imports.ui.extensionSystem;
const FileUtils = imports.misc.fileUtils;
const ModalDialog = imports.ui.modalDialog;
const _signals = ExtensionSystem._signals;
const REPOSITORY_URL_BASE = 'https://extensions.gnome.org';
const REPOSITORY_URL_DOWNLOAD = REPOSITORY_URL_BASE + '/download-extension/%s.shell-extension.zip';
const REPOSITORY_URL_INFO = REPOSITORY_URL_BASE + '/extension-info/';
let _httpSession;
function installExtensionFromUUID(uuid) {
let params = { uuid: uuid,
shell_version: Config.PACKAGE_VERSION };
let message = Soup.form_request_new_from_hash('GET', REPOSITORY_URL_INFO, params);
_httpSession.queue_message(message,
function(session, message) {
let info = JSON.parse(message.response_body.data);
let dialog = new InstallExtensionDialog(uuid, info);
dialog.open(global.get_current_time());
});
}
function uninstallExtensionFromUUID(uuid) {
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return false;
// Don't try to uninstall system extensions
if (extension.type != ExtensionUtils.ExtensionType.PER_USER)
return false;
if (!ExtensionSystem.unloadExtension(uuid))
return false;
FileUtils.recursivelyDeleteDir(extension.dir);
return true;
}
function gotExtensionZipFile(session, message, uuid) {
if (message.status_code != Soup.KnownStatusCode.OK) {
logExtensionError(uuid, 'downloading extension: ' + message.status_code);
return;
}
let dir = Gio.File.new_for_path(GLib.build_filenamev([global.userdatadir, 'extensions', uuid]));
try {
if (!dir.query_exists(null))
dir.make_directory_with_parents(null);
} catch (e) {
logExtensionError('Could not create extension directory');
}
let [file, stream] = Gio.File.new_tmp('XXXXXX.shell-extension.zip');
let contents = message.response_body.flatten().as_bytes();
stream.output_stream.write_bytes(contents, null);
stream.close(null);
let [success, pid] = GLib.spawn_async(null,
['unzip', '-uod', dir.get_path(), '--', file.get_path()],
null,
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
null);
if (!success) {
logExtensionError(uuid, 'extract: could not extract');
return;
}
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) {
GLib.spawn_close_pid(pid);
// Add extension to 'enabled-extensions' for the user, always...
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
if (enabledExtensions.indexOf(uuid) == -1) {
enabledExtensions.push(uuid);
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
}
ExtensionSystem.loadExtension(dir, ExtensionUtils.ExtensionType.PER_USER, true);
});
}
const InstallExtensionDialog = new Lang.Class({
Name: 'InstallExtensionDialog',
Extends: ModalDialog.ModalDialog,
_init: function(uuid, info) {
this.parent({ styleClass: 'extension-dialog' });
this._uuid = uuid;
this._info = info;
this.setButtons([{ label: _("Cancel"),
action: Lang.bind(this, this._onCancelButtonPressed),
key: Clutter.Escape
},
{ label: _("Install"),
action: Lang.bind(this, this._onInstallButtonPressed)
}]);
let message = _("Download and install '%s' from extensions.gnome.org?").format(info.name);
let box = new St.BoxLayout();
this.contentLayout.add(box);
let gicon = new Gio.FileIcon({ file: Gio.File.new_for_uri(REPOSITORY_URL_BASE + info.icon) })
let icon = new St.Icon({ gicon: gicon });
box.add(icon);
let label = new St.Label({ text: message });
box.add(label);
},
_onCancelButtonPressed: function(button, event) {
this.close(global.get_current_time());
// Even though the extension is already "uninstalled", send through
// a state-changed signal for any users who want to know if the install
// went through correctly -- using proper async DBus would block more
// traditional clients like the plugin
let meta = { uuid: this._uuid,
state: ExtensionSystem.ExtensionState.UNINSTALLED,
error: '' };
_signals.emit('extension-state-changed', meta);
},
_onInstallButtonPressed: function(button, event) {
let state = { uuid: this._uuid,
state: ExtensionSystem.ExtensionState.DOWNLOADING,
error: '' };
_signals.emit('extension-state-changed', state);
let params = { shell_version: Config.PACKAGE_VERSION };
let url = REPOSITORY_URL_DOWNLOAD.format(this._uuid);
let message = Soup.form_request_new_from_hash('GET', url, params);
_httpSession.queue_message(message,
Lang.bind(this, function(session, message) {
gotExtensionZipFile(session, message, this._uuid);
}));
this.close(global.get_current_time());
}
});
function init() {
_httpSession = new Soup.SessionAsync({ ssl_use_system_ca_file: true });
// See: https://bugzilla.gnome.org/show_bug.cgi?id=655189 for context.
// _httpSession.add_feature(new Soup.ProxyResolverDefault());
Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault());
}

View File

@ -3,19 +3,11 @@
const Lang = imports.lang;
const Signals = imports.signals;
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Soup = imports.gi.Soup;
const Config = imports.misc.config;
const ExtensionUtils = imports.misc.extensionUtils;
const FileUtils = imports.misc.fileUtils;
const ModalDialog = imports.ui.modalDialog;
const API_VERSION = 1;
const ExtensionState = {
ENABLED: 1,
@ -30,16 +22,6 @@ const ExtensionState = {
UNINSTALLED: 99
};
const REPOSITORY_URL_BASE = 'https://extensions.gnome.org';
const REPOSITORY_URL_DOWNLOAD = REPOSITORY_URL_BASE + '/download-extension/%s.shell-extension.zip';
const REPOSITORY_URL_INFO = REPOSITORY_URL_BASE + '/extension-info/';
const _httpSession = new Soup.SessionAsync({ ssl_use_system_ca_file: true });
// See: https://bugzilla.gnome.org/show_bug.cgi?id=655189 for context.
// _httpSession.add_feature(new Soup.ProxyResolverDefault());
Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault());
// Arrays of uuids
var enabledExtensions;
// Contains the order that extensions were enabled in.
@ -56,80 +38,6 @@ const disconnect = Lang.bind(_signals, _signals.disconnect);
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
function installExtensionFromUUID(uuid) {
let params = { uuid: uuid,
shell_version: Config.PACKAGE_VERSION };
let message = Soup.form_request_new_from_hash('GET', REPOSITORY_URL_INFO, params);
_httpSession.queue_message(message,
function(session, message) {
let info = JSON.parse(message.response_body.data);
let dialog = new InstallExtensionDialog(uuid, info);
dialog.open(global.get_current_time());
});
}
function uninstallExtensionFromUUID(uuid) {
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return false;
// Try to disable it -- if it's ERROR'd, we can't guarantee that,
// but it will be removed on next reboot, and hopefully nothing
// broke too much.
disableExtension(uuid);
// Don't try to uninstall system extensions
if (extension.type != ExtensionUtils.ExtensionType.PER_USER)
return false;
extension.state = ExtensionState.UNINSTALLED;
_signals.emit('extension-state-changed', extension);
delete ExtensionUtils.extensions[uuid];
FileUtils.recursivelyDeleteDir(Gio.file_new_for_path(extension.path));
return true;
}
function gotExtensionZipFile(session, message, uuid) {
if (message.status_code != Soup.KnownStatusCode.OK) {
logExtensionError(uuid, 'downloading extension: ' + message.status_code);
return;
}
let [file, stream] = Gio.File.new_tmp('XXXXXX.shell-extension.zip');
let dir = ExtensionUtils.userExtensionsDir.get_child(uuid);
let contents = message.response_body.flatten().as_bytes();
stream.output_stream.write_bytes(contents, null);
stream.close(null);
let [success, pid] = GLib.spawn_async(null,
['unzip', '-uod', dir.get_path(), '--', file.get_path()],
null,
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
null);
if (!success) {
logExtensionError(uuid, 'extract: could not extract');
return;
}
GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) {
GLib.spawn_close_pid(pid);
// Add extension to 'enabled-extensions' for the user, always...
let enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
if (enabledExtensions.indexOf(uuid) == -1) {
enabledExtensions.push(uuid);
global.settings.set_strv(ENABLED_EXTENSIONS_KEY, enabledExtensions);
}
loadExtension(dir, ExtensionUtils.ExtensionType.PER_USER, true);
});
}
function disableExtension(uuid) {
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
@ -159,6 +67,11 @@ function disableExtension(uuid) {
}
}
if (extension.stylesheet) {
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
theme.unload_stylesheet(extension.stylesheet.get_path());
}
try {
extension.stateObj.disable();
} catch(e) {
@ -201,6 +114,17 @@ function enableExtension(uuid) {
return;
}
let stylesheetFile = extension.dir.get_child('stylesheet.css');
if (stylesheetFile.query_exists(null)) {
let theme = St.ThemeContext.get_for_stage(global.stage).get_theme();
try {
theme.load_stylesheet(stylesheetFile.get_path());
extension.stylesheet = stylesheetFile;
} catch (e) {
logExtensionError(uuid, 'Stylesheet parse error: ' + e);
}
}
extension.state = ExtensionState.ENABLED;
_signals.emit('extension-state-changed', extension);
}
@ -221,35 +145,21 @@ function logExtensionError(uuid, message, state) {
state: state });
}
function loadExtension(dir, type, enabled) {
let uuid = dir.get_basename();
let extension;
if (ExtensionUtils.extensions[uuid] != undefined) {
log('Extension "%s" is already loaded'.format(uuid));
return;
}
try {
extension = ExtensionUtils.createExtensionObject(uuid, dir, type);
} catch(e) {
logExtensionError(uuid, e.message);
return;
}
function loadExtension(extension) {
// Default to error, we set success as the last step
extension.state = ExtensionState.ERROR;
if (ExtensionUtils.isOutOfDate(extension)) {
logExtensionError(uuid, 'extension is not compatible with current GNOME Shell and/or GJS version', ExtensionState.OUT_OF_DATE);
logExtensionError(extension.uuid, 'extension is not compatible with current GNOME Shell and/or GJS version', ExtensionState.OUT_OF_DATE);
extension.state = ExtensionState.OUT_OF_DATE;
return;
}
let enabled = enabledExtensions.indexOf(extension.uuid) != -1;
if (enabled) {
initExtension(uuid);
initExtension(extension.uuid);
if (extension.state == ExtensionState.DISABLED)
enableExtension(uuid);
enableExtension(extension.uuid);
} else {
extension.state = ExtensionState.INITIALIZED;
}
@ -257,6 +167,23 @@ function loadExtension(dir, type, enabled) {
_signals.emit('extension-state-changed', extension);
}
function unloadExtension(uuid) {
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return false;
// Try to disable it -- if it's ERROR'd, we can't guarantee that,
// but it will be removed on next reboot, and hopefully nothing
// broke too much.
disableExtension(uuid);
extension.state = ExtensionState.UNINSTALLED;
_signals.emit('extension-state-changed', extension);
delete ExtensionUtils.extensions[uuid];
return true;
}
function initExtension(uuid) {
let extension = ExtensionUtils.extensions[uuid];
let dir = extension.dir;
@ -269,18 +196,6 @@ function initExtension(uuid) {
logExtensionError(uuid, 'Missing extension.js');
return;
}
let stylesheetPath = null;
let themeContext = St.ThemeContext.get_for_stage(global.stage);
let theme = themeContext.get_theme();
let stylesheetFile = dir.get_child('stylesheet.css');
if (stylesheetFile.query_exists(null)) {
try {
theme.load_stylesheet(stylesheetFile.get_path());
} catch (e) {
logExtensionError(uuid, 'Stylesheet parse error: ' + e);
return;
}
}
let extensionModule;
let extensionState = null;
@ -288,24 +203,17 @@ function initExtension(uuid) {
ExtensionUtils.installImporter(extension);
extensionModule = extension.imports.extension;
} catch (e) {
if (stylesheetPath != null)
theme.unload_stylesheet(stylesheetPath);
logExtensionError(uuid, '' + e);
return;
}
if (!extensionModule.init) {
logExtensionError(uuid, 'missing \'init\' function');
return;
}
try {
extensionState = extensionModule.init(extension);
} catch (e) {
if (stylesheetPath != null)
theme.unload_stylesheet(stylesheetPath);
logExtensionError(uuid, 'Failed to evaluate init function:' + e);
return;
if (extensionModule.init) {
try {
extensionState = extensionModule.init(extension);
} catch (e) {
logExtensionError(uuid, 'Failed to evaluate init function:' + e);
return;
}
}
if (!extensionState)
@ -348,82 +256,13 @@ function onEnabledExtensionsChanged() {
enabledExtensions = newEnabledExtensions;
}
function init() {
ExtensionUtils.init();
function loadExtensions() {
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
}
function loadExtensions() {
ExtensionUtils.scanExtensions(function(uuid, dir, type) {
let enabled = enabledExtensions.indexOf(uuid) != -1;
loadExtension(dir, type, enabled);
let finder = new ExtensionUtils.ExtensionFinder();
finder.connect('extension-found', function(signals, extension) {
loadExtension(extension);
});
finder.scanExtensions();
}
const InstallExtensionDialog = new Lang.Class({
Name: 'InstallExtensionDialog',
Extends: ModalDialog.ModalDialog,
_init: function(uuid, info) {
this.parent({ styleClass: 'extension-dialog' });
this._uuid = uuid;
this._info = info;
this.setButtons([{ label: _("Cancel"),
action: Lang.bind(this, this._onCancelButtonPressed),
key: Clutter.Escape
},
{ label: _("Install"),
action: Lang.bind(this, this._onInstallButtonPressed)
}]);
let message = _("Download and install '%s' from extensions.gnome.org?").format(info.name);
let box = new St.BoxLayout();
this.contentLayout.add(box);
let gicon = new Gio.FileIcon({ file: Gio.File.new_for_uri(REPOSITORY_URL_BASE + info.icon) })
let icon = new St.Icon({ gicon: gicon });
box.add(icon);
let label = new St.Label({ text: message });
box.add(label);
},
_onCancelButtonPressed: function(button, event) {
this.close(global.get_current_time());
// Even though the extension is already "uninstalled", send through
// a state-changed signal for any users who want to know if the install
// went through correctly -- using proper async DBus would block more
// traditional clients like the plugin
let meta = { uuid: this._uuid,
state: ExtensionState.UNINSTALLED,
error: '' };
_signals.emit('extension-state-changed', meta);
},
_onInstallButtonPressed: function(button, event) {
let state = { uuid: this._uuid,
state: ExtensionState.DOWNLOADING,
error: '' };
_signals.emit('extension-state-changed', state);
let params = { shell_version: Config.PACKAGE_VERSION };
let url = REPOSITORY_URL_DOWNLOAD.format(this._uuid);
let message = Soup.form_request_new_from_hash('GET', url, params);
_httpSession.queue_message(message,
Lang.bind(this, function(session, message) {
gotExtensionZipFile(session, message, this._uuid);
}));
this.close(global.get_current_time());
}
});

View File

@ -309,21 +309,22 @@ const IconGrid = new Lang.Class({
this._grid.queue_relayout();
},
removeAll: function () {
this._grid.get_children().forEach(Lang.bind(this, function (child) {
child.destroy();
}));
removeAll: function() {
this._grid.destroy_all_children();
},
addItem: function(actor) {
this._grid.add_actor(actor);
addItem: function(actor, index) {
if (index !== undefined)
this._grid.insert_child_at_index(actor, index);
else
this._grid.add_actor(actor);
},
getItemAtIndex: function(index) {
return this._grid.get_children()[index];
return this._grid.get_child_at_index(index);
},
visibleItemsCount: function() {
return this._grid.get_children().length - this._grid.get_n_skip_paint();
return this._grid.get_n_children() - this._grid.get_n_skip_paint();
}
});

View File

@ -53,6 +53,10 @@ const LayoutManager = new Lang.Class({
global.screen.connect('monitors-changed',
Lang.bind(this, this._monitorsChanged));
this._monitorsChanged();
this._chrome.connect('primary-fullscreen-changed', Lang.bind(this, function(chrome, state) {
this.emit('primary-fullscreen-changed', state);
}));
},
// This is called by Main after everything else is constructed;
@ -224,26 +228,9 @@ const LayoutManager = new Lang.Class({
return false;
},
get focusIndex() {
let focusWindow = global.display.focus_window;
if (focusWindow) {
let wrect = focusWindow.get_outer_rect();
for (let i = 0; i < this.monitors.length; i++) {
let monitor = this.monitors[i];
if (monitor.x <= wrect.x && monitor.y <= wrect.y &&
monitor.x + monitor.width > wrect.x &&
monitor.y + monitor.height > wrect.y)
return i;
}
}
return this.primaryIndex;
},
get focusMonitor() {
return this.monitors[this.focusIndex];
get currentMonitor() {
let index = global.screen.get_current_monitor();
return Main.layoutManager.monitors[index];
},
_startupAnimation: function() {
@ -852,6 +839,8 @@ const Chrome = new Lang.Class({
for (let i = 0; i < this._monitors.length; i++)
wasInFullscreen[i] = this._monitors[i].inFullscreen;
let primaryWasInFullscreen = this._primaryMonitor.inFullscreen;
this._updateFullscreen();
let changed = false;
@ -861,10 +850,15 @@ const Chrome = new Lang.Class({
break;
}
}
if (changed) {
this._updateVisibility();
this._queueUpdateRegions();
}
if (primaryWasInFullscreen != this._primaryMonitor.inFullscreen) {
this.emit('primary-fullscreen-changed', this._primaryMonitor.inFullscreen);
}
},
updateRegions: function() {
@ -979,3 +973,5 @@ const Chrome = new Lang.Class({
return false;
}
});
Signals.addSignalMethods(Chrome.prototype);

View File

@ -38,7 +38,7 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
'const stage = global.stage; ' +
'const color = function(pixel) { let c= new Clutter.Color(); c.from_pixel(pixel); return c; }; ' +
/* Special lookingGlass functions */
'const it = Main.lookingGlass.getIt(); ' +
'const it = Main.lookingGlass.getIt(); ' +
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
const HISTORY_KEY = 'looking-glass-history';
@ -320,7 +320,7 @@ const WindowList = new Lang.Class({
},
_updateWindowList: function() {
this.actor.get_children().forEach(function (actor) { actor.destroy(); });
this.actor.destroy_all_children();
let windows = global.get_window_actors();
let tracker = Shell.WindowTracker.get_default();
for (let i = 0; i < windows.length; i++) {
@ -378,7 +378,7 @@ const ObjInspector = new Lang.Class({
this._previousObj = null;
this._obj = obj;
this._container.get_children().forEach(function (child) { child.destroy(); });
this._container.destroy_all_children();
let hbox = new St.BoxLayout({ style_class: 'lg-obj-inspector-title' });
this._container.add_actor(hbox);

View File

@ -18,6 +18,7 @@ const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
const KeyringPrompt = imports.ui.keyringPrompt;
const Environment = imports.ui.environment;
const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionDownloader = imports.ui.extensionDownloader;
const Keyboard = imports.ui.keyboard;
const MessageTray = imports.ui.messageTray;
const Overview = imports.ui.overview;
@ -32,6 +33,7 @@ const WindowAttentionHandler = imports.ui.windowAttentionHandler;
const Scripting = imports.ui.scripting;
const SessionMode = imports.ui.sessionMode;
const ShellDBus = imports.ui.shellDBus;
const ShellMountOperation = imports.ui.shellMountOperation;
const TelepathyClient = imports.ui.telepathyClient;
const WindowManager = imports.ui.windowManager;
const Magnifier = imports.ui.magnifier;
@ -59,6 +61,7 @@ let ctrlAltTabManager = null;
let recorder = null;
let sessionMode = null;
let shellDBusService = null;
let shellMountOpDBusService = null;
let modalCount = 0;
let modalActorFocusStack = [];
let uiGroup = null;
@ -149,6 +152,7 @@ function start() {
sessionMode = new SessionMode.SessionMode();
shellDBusService = new ShellDBus.GnomeShell();
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
// also initialize ShellAppSystem first. ShellAppSystem
@ -220,7 +224,7 @@ function start() {
false, -1, 1);
if (sessionMode.allowExtensions) {
ExtensionSystem.init();
ExtensionDownloader.init();
ExtensionSystem.loadExtensions();
}
@ -670,6 +674,7 @@ function pushModal(actor, timestamp, options) {
log('pushModal: invocation of begin_modal failed');
return false;
}
Meta.disable_unredirect_for_screen(global.screen);
}
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
@ -750,6 +755,7 @@ function popModal(actor, timestamp) {
global.end_modal(timestamp);
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
Meta.enable_unredirect_for_screen(global.screen);
}
function createLookingGlass() {

View File

@ -756,7 +756,7 @@ const Notification = new Lang.Class({
button.label = label;
}
if (this._buttonBox.get_children().length > 0)
if (this._buttonBox.get_n_children() > 0)
this._buttonFocusManager.remove_group(this._buttonBox);
this._buttonBox.add(button);
@ -1284,7 +1284,7 @@ const SummaryItem = new Lang.Class({
},
prepareNotificationStackForShowing: function() {
if (this.notificationStack.get_children().length > 0)
if (this.notificationStack.get_n_children() > 0)
return;
for (let i = 0; i < this.source.notifications.length; i++) {
@ -1293,7 +1293,6 @@ const SummaryItem = new Lang.Class({
},
doneShowingNotificationStack: function() {
let notificationActors = this.notificationStack.get_children();
for (let i = 0; i < this._stackedNotifications.length; i++) {
let stackedNotification = this._stackedNotifications[i];
let notification = stackedNotification.notification;
@ -1323,7 +1322,7 @@ const SummaryItem = new Lang.Class({
this._stackedNotifications.push(stackedNotification);
if (!this.source.isChat)
notification.enableScrolling(false);
if (this.notificationStack.get_children().length > 0)
if (this.notificationStack.get_n_children() > 0)
notification.setIconVisible(false);
this.notificationStack.add(notification.actor);
notification.expand(false);
@ -1469,6 +1468,7 @@ const MessageTray = new Lang.Class({
this._overviewVisible = Main.overview.visible;
this._notificationRemoved = false;
this._reNotifyAfterHideNotification = null;
this._inFullscreen = false;
this._corner = new Clutter.Rectangle({ width: 1,
height: 1,
@ -1484,6 +1484,7 @@ const MessageTray = new Lang.Class({
Main.layoutManager.trackChrome(this._notificationBin);
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._setSizePosition));
Main.layoutManager.connect('primary-fullscreen-changed', Lang.bind(this, this._onFullscreenChanged));
this._setSizePosition();
@ -1957,6 +1958,11 @@ const MessageTray = new Lang.Class({
this._updateState();
},
_onFullscreenChanged: function(obj, state) {
this._inFullscreen = state;
this._updateState();
},
_onStatusChanged: function(status) {
if (status == GnomeSession.PresenceStatus.BUSY) {
// remove notification and allow the summary to be closed now
@ -2014,7 +2020,7 @@ const MessageTray = new Lang.Class({
_updateState: function() {
// Notifications
let notificationUrgent = this._notificationQueue.length > 0 && this._notificationQueue[0].urgency == Urgency.CRITICAL;
let notificationsPending = this._notificationQueue.length > 0 && (!this._busy || notificationUrgent);
let notificationsPending = this._notificationQueue.length > 0 && ((!this._busy && !this._inFullscreen) || notificationUrgent);
let notificationPinned = this._pointerInTray && !this._pointerInSummary && !this._notificationRemoved;
let notificationExpanded = this._notificationBin.y < 0;
let notificationExpired = (this._notificationTimeoutId == 0 && !(this._notification && this._notification.urgency == Urgency.CRITICAL) && !this._pointerInTray && !this._locked && !(this._pointerInKeyboard && notificationExpanded)) || this._notificationRemoved;
@ -2050,7 +2056,7 @@ const MessageTray = new Lang.Class({
if (this._summaryState == State.HIDDEN && !mustHideSummary) {
if (summarySummoned) {
this._showSummary(0);
} else if (notificationsDone && !this._busy) {
} else if (notificationsDone && !this._busy && !this._inFullscreen) {
if (this._backFromAway && this._unseenNotifications.length > 0)
this._showSummary(LONGER_SUMMARY_TIMEOUT);
else if (this._newSummaryItems.length > 0)
@ -2429,7 +2435,7 @@ const MessageTray = new Lang.Class({
},
_onSummaryBoxPointerContentUpdated: function() {
if (this._summaryBoxPointerItem.notificationStack.get_children().length == 0)
if (this._summaryBoxPointerItem.notificationStack.get_n_children() == 0)
this._hideSummaryBoxPointer();
this._adjustSummaryBoxPointerPosition();
@ -2463,7 +2469,7 @@ const MessageTray = new Lang.Class({
// We should be sure to hide the box pointer if all notifications in it are destroyed while
// it is hiding, so that we don't show an an animation of an empty blob being hidden.
if (this._summaryBoxPointerState == State.HIDING &&
this._summaryBoxPointerItem.notificationStack.get_children().length == 0) {
this._summaryBoxPointerItem.notificationStack.get_n_children() == 0) {
this._summaryBoxPointer.actor.hide();
return;
}

View File

@ -180,7 +180,7 @@ const ModalDialog = new Lang.Class({
},
_fadeOpen: function() {
let monitor = Main.layoutManager.focusMonitor;
let monitor = Main.layoutManager.currentMonitor;
this._backgroundBin.set_position(monitor.x, monitor.y);
this._backgroundBin.set_size(monitor.width, monitor.height);

View File

@ -1,6 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Config = imports.misc.config;
const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta;
const Mainloop = imports.mainloop;
@ -11,10 +12,9 @@ const Shell = imports.gi.Shell;
const Gdk = imports.gi.Gdk;
const AppDisplay = imports.ui.appDisplay;
const ContactDisplay = imports.ui.contactDisplay;
const ContactDisplay = Config.HAVE_FOLKS ? imports.ui.contactDisplay : null;
const Dash = imports.ui.dash;
const DND = imports.ui.dnd;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const Panel = imports.ui.panel;
@ -208,7 +208,8 @@ const Overview = new Lang.Class({
this.addSearchProvider(new AppDisplay.AppSearchProvider());
this.addSearchProvider(new AppDisplay.SettingsSearchProvider());
this.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
this.addSearchProvider(new ContactDisplay.ContactSearchProvider());
if (ContactDisplay != null)
this.addSearchProvider(new ContactDisplay.ContactSearchProvider());
// Load remote search providers provided by applications
RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, this.addSearchProvider));

View File

@ -906,7 +906,7 @@ const PopupMenuBase = new Lang.Class({
},
isEmpty: function() {
return this.box.get_children().length == 0;
return this.box.get_n_children() == 0;
},
isChildMenu: function(menu) {

View File

@ -62,10 +62,25 @@ function loadRemoteSearchProvidersFromDir(dir, addProviderCallback) {
let remoteProvider, title;
try {
let group = KEY_FILE_GROUP;
let icon = keyfile.get_string(group, 'Icon');
let busName = keyfile.get_string(group, 'BusName');
let objectPath = keyfile.get_string(group, 'ObjectPath');
title = keyfile.get_locale_string(group, 'Title', null);
let appInfo = null;
try {
let desktopId = keyfile.get_string(group, 'DesktopId');
appInfo = Gio.DesktopAppInfo.new(desktopId);
} catch (e) {
}
let icon;
if (appInfo) {
icon = appInfo.get_icon();
title = appInfo.get_name();
} else {
let iconName = keyfile.get_string(group, 'Icon');
icon = new Gio.ThemedIcon({ name: iconName });
title = keyfile.get_locale_string(group, 'Title', null);
}
remoteProvider = new RemoteSearchProvider(title,
icon,

View File

@ -51,7 +51,7 @@ const SearchResultDisplay = new Lang.Class({
* Remove all results from this display.
*/
clear: function() {
this.actor.get_children().forEach(function (actor) { actor.destroy(); });
this.actor.destroy_all_children();
},
/**

View File

@ -7,6 +7,7 @@ const Shell = imports.gi.Shell;
const Config = imports.misc.config;
const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionDownloader = imports.ui.extensionDownloader;
const ExtensionUtils = imports.misc.extensionUtils;
const Flashspot = imports.ui.flashspot;
const Main = imports.ui.main;
@ -66,6 +67,9 @@ const GnomeShellIface = <interface name="org.gnome.Shell">
<method name="LaunchExtensionPrefs">
<arg type="s" direction="in" name="uuid"/>
</method>
<method name="ReloadExtension">
<arg type="s" direction="in" name="uuid"/>
</method>
<property name="OverviewActive" type="b" access="readwrite" />
<property name="ApiVersion" type="i" access="read" />
<property name="ShellVersion" type="s" access="read" />
@ -254,11 +258,11 @@ const GnomeShell = new Lang.Class({
},
InstallRemoteExtension: function(uuid) {
ExtensionSystem.installExtensionFromUUID(uuid);
ExtensionDownloader.installExtensionFromUUID(uuid);
},
UninstallExtension: function(uuid) {
return ExtensionSystem.uninstallExtensionFromUUID(uuid);
return ExtensionDownloader.uninstallExtensionFromUUID(uuid);
},
LaunchExtensionPrefs: function(uuid) {
@ -268,6 +272,11 @@ const GnomeShell = new Lang.Class({
['extension:///' + uuid], -1, null);
},
ReloadExtension: function(uuid) {
ExtensionSystem.unloadExtension(uuid);
ExtensionSystem.loadExtension(uuid);
},
get OverviewActive() {
return Main.overview.visible;
},
@ -279,8 +288,6 @@ const GnomeShell = new Lang.Class({
Main.overview.hide();
},
ApiVersion: ExtensionSystem.API_VERSION,
ShellVersion: Config.PACKAGE_VERSION,
_extensionStateChanged: function(_, newState) {

View File

@ -1,17 +1,21 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Signals = imports.signals;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Pango = imports.gi.Pango;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const CheckBox = imports.ui.checkBox;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const ModalDialog = imports.ui.modalDialog;
const Params = imports.misc.params;
const ShellEntry = imports.ui.shellEntry;
const LIST_ITEM_ICON_SIZE = 48;
@ -48,6 +52,11 @@ function _setLabelsForMessage(dialog, message) {
_setLabelText(dialog.descriptionLabel, labels[1]);
}
function _createIcon(gicon) {
return new St.Icon({ gicon: gicon,
style_class: 'shell-mount-operation-icon' })
}
/* -------------------------------------------------------- */
const ListItem = new Lang.Class({
@ -91,11 +100,11 @@ const ShellMountOperation = new Lang.Class({
Name: 'ShellMountOperation',
_init: function(source, params) {
params = Params.parse(params, { reaskPassword: false });
this._reaskPassword = params.reaskPassword;
params = Params.parse(params, { existingDialog: null });
this._dialog = null;
this._dialogId = 0;
this._existingDialog = params.existingDialog;
this._processesDialog = null;
this.mountOp = new Shell.MountOperation();
@ -107,99 +116,120 @@ const ShellMountOperation = new Lang.Class({
this.mountOp.connect('show-processes-2',
Lang.bind(this, this._onShowProcesses2));
this.mountOp.connect('aborted',
Lang.bind(this, this._onAborted));
Lang.bind(this, this.close));
this._icon = new St.Icon({ gicon: source.get_icon(),
style_class: 'shell-mount-operation-icon' });
this._gicon = source.get_icon();
},
_closeExistingDialog: function() {
if (!this._existingDialog)
return;
this._existingDialog.close();
this._existingDialog = null;
},
_onAskQuestion: function(op, message, choices) {
this._dialog = new ShellMountQuestionDialog(this._icon);
this._closeExistingDialog();
this._dialog = new ShellMountQuestionDialog(this._gicon);
this._dialog.connect('response',
Lang.bind(this, function(object, choice) {
this.mountOp.set_choice(choice);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
this._dialogId = this._dialog.connect('response', Lang.bind(this,
function(object, choice) {
this.mountOp.set_choice(choice);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
this._dialog.close(global.get_current_time());
this._dialog = null;
}));
this.close();
}));
this._dialog.update(message, choices);
this._dialog.open(global.get_current_time());
this._dialog.open();
},
_onAskPassword: function(op, message) {
this._notificationShowing = true;
this._source = new ShellMountPasswordSource(message, this._icon, this._reaskPassword);
_onAskPassword: function(op, message, defaultUser, defaultDomain, flags) {
if (this._existingDialog) {
this._dialog = this._existingDialog;
this._dialog.reaskPassword();
} else {
this._dialog = new ShellMountPasswordDialog(message, this._gicon, flags);
}
this._source.connect('password-ready',
Lang.bind(this, function(source, password) {
this.mountOp.set_password(password);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
this._dialogId = this._dialog.connect('response', Lang.bind(this,
function(object, choice, password, remember) {
if (choice == -1) {
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
} else {
if (remember)
this.mountOp.set_password_save(Gio.PasswordSave.PERMANENTLY);
else
this.mountOp.set_password_save(Gio.PasswordSave.NEVER);
this._notificationShowing = false;
this._source.destroy();
}));
this._source.connect('destroy',
Lang.bind(this, function() {
if (!this._notificationShowing)
return;
this._notificationShowing = false;
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
}));
this.mountOp.set_password(password);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
}
}));
this._dialog.open();
},
_onAborted: function(op) {
if (!this._dialog)
return;
close: function(op) {
this._closeExistingDialog();
this._processesDialog = null;
this._dialog.close(global.get_current_time());
this._dialog = null;
if (this._dialog) {
this._dialog.close();
this._dialog = null;
}
},
_onShowProcesses2: function(op) {
this._closeExistingDialog();
let processes = op.get_show_processes_pids();
let choices = op.get_show_processes_choices();
let message = op.get_show_processes_message();
if (!this._processesDialog) {
this._processesDialog = new ShellProcessesDialog(this._icon);
this._processesDialog = new ShellProcessesDialog(this._gicon);
this._dialog = this._processesDialog;
this._processesDialog.connect('response',
Lang.bind(this, function(object, choice) {
if (choice == -1) {
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
} else {
this.mountOp.set_choice(choice);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
}
this._dialogId = this._processesDialog.connect('response', Lang.bind(this,
function(object, choice) {
if (choice == -1) {
this.mountOp.reply(Gio.MountOperationResult.ABORTED);
} else {
this.mountOp.set_choice(choice);
this.mountOp.reply(Gio.MountOperationResult.HANDLED);
}
this._processesDialog.close(global.get_current_time());
this._dialog = null;
}));
this._processesDialog.open(global.get_current_time());
this.close();
}));
this._processesDialog.open();
}
this._processesDialog.update(message, processes, choices);
},
borrowDialog: function() {
if (this._dialogId != 0) {
this._dialog.disconnect(this._dialogId);
this._dialogId = 0;
}
return this._dialog;
}
});
const ShellMountQuestionDialog = new Lang.Class({
Name: 'ShellMountQuestionDialog',
Extends: ModalDialog.ModalDialog,
_init: function(icon) {
_init: function(gicon) {
this.parent({ styleClass: 'mount-question-dialog' });
let mainContentLayout = new St.BoxLayout();
this.contentLayout.add(mainContentLayout, { x_fill: true,
y_fill: false });
this._iconBin = new St.Bin({ child: icon });
this._iconBin = new St.Bin({ child: _createIcon(gicon) });
mainContentLayout.add(this._iconBin,
{ x_fill: true,
y_fill: false,
@ -234,63 +264,107 @@ const ShellMountQuestionDialog = new Lang.Class({
});
Signals.addSignalMethods(ShellMountQuestionDialog.prototype);
const ShellMountPasswordSource = new Lang.Class({
Name: 'ShellMountPasswordSource',
Extends: MessageTray.Source,
const ShellMountPasswordDialog = new Lang.Class({
Name: 'ShellMountPasswordDialog',
Extends: ModalDialog.ModalDialog,
_init: function(message, icon, reaskPassword) {
_init: function(message, gicon, flags) {
let strings = message.split('\n');
this.parent(strings[0]);
this.parent({ styleClass: 'prompt-dialog' });
this._notification = new ShellMountPasswordNotification(this, strings, icon, reaskPassword);
this._setSummaryIcon(icon);
let mainContentBox = new St.BoxLayout({ style_class: 'prompt-dialog-main-layout',
vertical: false });
this.contentLayout.add(mainContentBox);
// add ourselves as a source, and popup the notification
Main.messageTray.add(this);
this.notify(this._notification);
},
});
Signals.addSignalMethods(ShellMountPasswordSource.prototype);
let icon = _createIcon(gicon);
mainContentBox.add(icon,
{ x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.START });
const ShellMountPasswordNotification = new Lang.Class({
Name: 'ShellMountPasswordNotification',
Extends: MessageTray.Notification,
this._messageBox = new St.BoxLayout({ style_class: 'prompt-dialog-message-layout',
vertical: true });
mainContentBox.add(this._messageBox,
{ y_align: St.Align.START, expand: true, x_fill: true, y_fill: true });
_init: function(source, strings, icon, reaskPassword) {
this.parent(source, strings[0], null, { customContent: true, icon: icon });
// set the notification to transient and urgent, so that it
// expands out
this.setTransient(true);
this.setUrgency(MessageTray.Urgency.CRITICAL);
let subject = new St.Label({ style_class: 'prompt-dialog-headline' });
this._messageBox.add(subject,
{ y_fill: false,
y_align: St.Align.START });
if (strings[0])
subject.set_text(strings[0]);
let description = new St.Label({ style_class: 'prompt-dialog-description' });
description.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
description.clutter_text.line_wrap = true;
this._messageBox.add(description,
{ y_fill: true,
y_align: St.Align.START });
if (strings[1])
this.addBody(strings[1]);
description.set_text(strings[1]);
if (reaskPassword) {
let label = new St.Label({ style_class: 'mount-password-reask',
text: _("Wrong password, please try again") });
this._passwordBox = new St.BoxLayout({ vertical: false });
this._messageBox.add(this._passwordBox);
this.addActor(label);
this._passwordLabel = new St.Label(({ style_class: 'prompt-dialog-password-label',
text: _("Passphrase") }));
this._passwordBox.add(this._passwordLabel);
this._passwordEntry = new St.Entry({ style_class: 'prompt-dialog-password-entry',
text: "",
can_focus: true});
ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true });
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate));
this._passwordEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
this._passwordBox.add(this._passwordEntry, {expand: true });
this.setInitialKeyFocus(this._passwordEntry);
this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label',
text: _("Sorry, that didn\'t work. Please try again.") });
this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._errorMessageLabel.clutter_text.line_wrap = true;
this._errorMessageLabel.hide();
this._messageBox.add(this._errorMessageLabel);
if (flags & Gio.AskPasswordFlags.SAVING_SUPPORTED) {
this._rememberChoice = new CheckBox.CheckBox();
this._rememberChoice.getLabelActor().text = _("Remember Passphrase");
this._rememberChoice.actor.checked = true;
this._messageBox.add(this._rememberChoice.actor);
} else {
this._rememberChoice = null;
}
this._responseEntry = new St.Entry({ style_class: 'mount-password-entry',
can_focus: true });
this.setActionArea(this._responseEntry);
let buttons = [{ label: _("Cancel"),
action: Lang.bind(this, this._onCancelButton),
key: Clutter.Escape
},
{ label: _("Unlock"),
action: Lang.bind(this, this._onUnlockButton)
}];
this._responseEntry.clutter_text.connect('activate',
Lang.bind(this, this._onEntryActivated));
this._responseEntry.clutter_text.set_password_char('\u25cf'); // ● U+25CF BLACK CIRCLE
this._responseEntry.grab_key_focus();
this.setButtons(buttons);
},
_onEntryActivated: function() {
let text = this._responseEntry.get_text();
if (text == '')
return;
reaskPassword: function() {
this._passwordEntry.set_text('');
this._errorMessageLabel.show();
},
this.source.emit('password-ready', text);
_onCancelButton: function() {
this.emit('response', -1, '', false);
},
_onUnlockButton: function() {
this._onEntryActivate();
},
_onEntryActivate: function() {
this.emit('response', 1,
this._passwordEntry.get_text(),
this._rememberChoice &&
this._rememberChoice.actor.checked);
}
});
@ -298,14 +372,14 @@ const ShellProcessesDialog = new Lang.Class({
Name: 'ShellProcessesDialog',
Extends: ModalDialog.ModalDialog,
_init: function(icon) {
_init: function(gicon) {
this.parent({ styleClass: 'show-processes-dialog' });
let mainContentLayout = new St.BoxLayout();
this.contentLayout.add(mainContentLayout, { x_fill: true,
y_fill: false });
this._iconBin = new St.Bin({ child: icon });
this._iconBin = new St.Bin({ child: _createIcon(gicon) });
mainContentLayout.add(this._iconBin,
{ x_fill: true,
y_fill: false,
@ -347,13 +421,13 @@ const ShellProcessesDialog = new Lang.Class({
this._applicationList.connect('actor-added',
Lang.bind(this, function() {
if (this._applicationList.get_children().length == 1)
if (this._applicationList.get_n_children() == 1)
scrollView.show();
}));
this._applicationList.connect('actor-removed',
Lang.bind(this, function() {
if (this._applicationList.get_children().length == 0)
if (this._applicationList.get_n_children() == 0)
scrollView.hide();
}));
},
@ -387,3 +461,253 @@ const ShellProcessesDialog = new Lang.Class({
}
});
Signals.addSignalMethods(ShellProcessesDialog.prototype);
const GnomeShellMountOpIface = <interface name="org.Gtk.MountOperationHandler">
<method name="AskPassword">
<arg type="s" direction="in" name="object_id"/>
<arg type="s" direction="in" name="message"/>
<arg type="s" direction="in" name="icon_name"/>
<arg type="s" direction="in" name="default_user"/>
<arg type="s" direction="in" name="default_domain"/>
<arg type="u" direction="in" name="flags"/>
<arg type="u" direction="out" name="response"/>
<arg type="a{sv}" direction="out" name="response_details"/>
</method>
<method name="AskQuestion">
<arg type="s" direction="in" name="object_id"/>
<arg type="s" direction="in" name="message"/>
<arg type="s" direction="in" name="icon_name"/>
<arg type="as" direction="in" name="choices"/>
<arg type="u" direction="out" name="response"/>
<arg type="a{sv}" direction="out" name="response_details"/>
</method>
<method name="ShowProcesses">
<arg type="s" direction="in" name="object_id"/>
<arg type="s" direction="in" name="message"/>
<arg type="s" direction="in" name="icon_name"/>
<arg type="ai" direction="in" name="application_pids"/>
<arg type="as" direction="in" name="choices"/>
<arg type="u" direction="out" name="response"/>
<arg type="a{sv}" direction="out" name="response_details"/>
</method>
<method name="Close"/>
</interface>;
const ShellMountOperationType = {
NONE: 0,
ASK_PASSWORD: 1,
ASK_QUESTION: 2,
SHOW_PROCESSES: 3
};
const GnomeShellMountOpHandler = new Lang.Class({
Name: 'GnomeShellMountOpHandler',
_init: function() {
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellMountOpIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gtk/MountOperationHandler');
Gio.bus_own_name_on_connection(Gio.DBus.session, 'org.gtk.MountOperationHandler',
Gio.BusNameOwnerFlags.REPLACE, null, null);
this._dialog = null;
this._volumeMonitor = Gio.VolumeMonitor.get();
this._ensureEmptyRequest();
},
_ensureEmptyRequest: function() {
this._currentId = null;
this._currentInvocation = null;
this._currentType = ShellMountOperationType.NONE;
},
_clearCurrentRequest: function(response, details) {
if (this._currentInvocation) {
this._currentInvocation.return_value(
GLib.Variant.new('(ua{sv})', [response, details]));
}
this._ensureEmptyRequest();
},
_setCurrentRequest: function(invocation, id, type) {
let oldId = this._currentId;
let oldType = this._currentType;
let requestId = id + '@' + invocation.get_sender();
this._clearCurrentRequest(Gio.MountOperationResult.UNHANDLED, {});
this._currentInvocation = invocation;
this._currentId = requestId;
this._currentType = type;
if (this._dialog && (oldId == requestId) && (oldType == type))
return true;
return false;
},
_closeDialog: function() {
if (this._dialog) {
this._dialog.close();
this._dialog = null;
}
},
_createGIcon: function(iconName) {
let realIconName = iconName ? iconName : 'drive-harddisk';
return new Gio.ThemedIcon({ name: realIconName,
use_default_fallbacks: true });
},
/**
* AskPassword:
* @id: an opaque ID identifying the object for which the operation is requested
* The ID must be unique in the context of the calling process.
* @message: the message to display
* @icon_name: the name of an icon to display
* @default_user: the default username for display
* @default_domain: the default domain for display
* @flags: a set of GAskPasswordFlags
* @response: a GMountOperationResult
* @response_details: a dictionary containing the response details as
* entered by the user. The dictionary MAY contain the following properties:
* - "password" -> (s): a password to be used to complete the mount operation
* - "password_save" -> (u): a GPasswordSave
*
* The dialog will stay visible until clients call the Close() method, or
* another dialog becomes visible.
* Calling AskPassword again for the same id will have the effect to clear
* the existing dialog and update it with a message indicating the previous
* attempt went wrong.
*/
AskPasswordAsync: function(params, invocation) {
let [id, message, iconName, defaultUser, defaultDomain, flags] = params;
if (this._setCurrentRequest(invocation, id, ShellMountOperationType.ASK_PASSWORD)) {
this._dialog.reaskPassword();
return;
}
this._closeDialog();
this._dialog = new ShellMountPasswordDialog(message, this._createGIcon(iconName), flags);
this._dialog.connect('response', Lang.bind(this,
function(object, choice, password, remember) {
let details = {};
let response;
if (choice == -1) {
response = Gio.MountOperationResult.ABORTED;
} else {
response = Gio.MountOperationResult.HANDLED;
let passSave = remember ? Gio.PasswordSave.PERMANENTLY : Gio.PasswordSave.NEVER;
details['password_save'] = GLib.Variant.new('u', passSave);
details['password'] = GLib.Variant.new('s', password);
}
this._clearCurrentRequest(response, details);
}));
this._dialog.open();
},
/**
* AskQuestion:
* @id: an opaque ID identifying the object for which the operation is requested
* The ID must be unique in the context of the calling process.
* @message: the message to display
* @icon_name: the name of an icon to display
* @choices: an array of choice strings
* GetResponse:
* @response: a GMountOperationResult
* @response_details: a dictionary containing the response details as
* entered by the user. The dictionary MAY contain the following properties:
* - "choice" -> (i): the chosen answer among the array of strings passed in
*
* The dialog will stay visible until clients call the Close() method, or
* another dialog becomes visible.
* Calling AskQuestion again for the same id will have the effect to clear
* update the dialog with the new question.
*/
AskQuestionAsync: function(params, invocation) {
let [id, message, iconName, choices] = params;
if (this._setCurrentRequest(invocation, id, ShellMountOperationType.ASK_QUESTION)) {
this._dialog.update(message, choices);
return;
}
this._closeDialog();
this._dialog = new ShellMountQuestionDialog(this._createGIcon(iconName), message);
this._dialog.connect('response', Lang.bind(this,
function(object, choice) {
this._clearCurrentRequest(Gio.MountOperationResult.HANDLED,
{ choice: GLib.Variant.new('i', choice) });
}));
this._dialog.update(message, choices);
this._dialog.open();
},
/**
* ShowProcesses:
* @id: an opaque ID identifying the object for which the operation is requested
* The ID must be unique in the context of the calling process.
* @message: the message to display
* @icon_name: the name of an icon to display
* @application_pids: the PIDs of the applications to display
* @choices: an array of choice strings
* @response: a GMountOperationResult
* @response_details: a dictionary containing the response details as
* entered by the user. The dictionary MAY contain the following properties:
* - "choice" -> (i): the chosen answer among the array of strings passed in
*
* The dialog will stay visible until clients call the Close() method, or
* another dialog becomes visible.
* Calling ShowProcesses again for the same id will have the effect to clear
* the existing dialog and update it with the new message and the new list
* of processes.
*/
ShowProcessesAsync: function(params, invocation) {
let [id, message, iconName, applicationPids, choices] = params;
if (this._setCurrentRequest(invocation, id, ShellMountOperationType.SHOW_PROCESSES)) {
this._dialog.update(message, applicationPids, choices);
return;
}
this._closeDialog();
this._dialog = new ShellProcessesDialog(this._createGIcon(iconName));
this._dialog.connect('response', Lang.bind(this,
function(object, choice) {
let response;
let details = {};
if (choice == -1) {
response = Gio.MountOperationResult.ABORTED;
} else {
response = Gio.MountOperationResult.HANDLED;
details['choice'] = GLib.Variant.new('i', choice);
}
this._clearCurrentRequest(response, details);
}));
this._dialog.update(message, applicationPids, choices);
this._dialog.open();
},
/**
* Close:
*
* Closes a dialog previously opened by AskPassword, AskQuestion or ShowProcesses.
* If no dialog is open, does nothing.
*/
Close: function(params, invocation) {
this._clearCurrentRequest(Gio.MountOperationResult.UNHANDLED, {});
this._closeDialog();
}
});

View File

@ -56,9 +56,9 @@ const ATIndicator = new Lang.Class({
let textZoom = this._buildFontItem();
this.menu.addMenuItem(textZoom);
// let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA,
// 'screen-reader-enabled');
// this.menu.addMenuItem(screenReader);
let screenReader = this._buildItem(_("Screen Reader"), APPLICATIONS_SCHEMA,
'screen-reader-enabled');
this.menu.addMenuItem(screenReader);
let screenKeyboard = this._buildItem(_("Screen Keyboard"), APPLICATIONS_SCHEMA,
'screen-keyboard-enabled');

View File

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

View File

@ -132,6 +132,9 @@ const Client = new Lang.Class({
let channel = channels[i];
let [targetHandle, targetHandleType] = channel.get_handle();
if (Shell.is_channel_invalidated(channel))
continue;
/* Only observe contact text channels */
if ((!(channel instanceof Tp.TextChannel)) ||
targetHandleType != Tp.HandleType.CONTACT)
@ -181,6 +184,9 @@ const Client = new Lang.Class({
continue;
}
if (Shell.is_channel_invalidated(channel))
continue;
// 'notify' will be true when coming from an actual HandleChannels
// call, and not when from a successful Claim call. The point is
// we don't want to notify for a channel we just claimed which
@ -231,12 +237,19 @@ const Client = new Lang.Class({
let channel = channels[0];
let chanType = channel.get_channel_type();
if (Shell.is_channel_invalidated(channel)) {
Shell.decline_dispatch_op(context, 'Channel is invalidated');
return;
}
if (chanType == Tp.IFACE_CHANNEL_TYPE_TEXT)
this._approveTextChannel(account, conn, channel, dispatchOp, context);
else if (chanType == Tp.IFACE_CHANNEL_TYPE_CALL)
this._approveCall(account, conn, channel, dispatchOp, context);
else if (chanType == Tp.IFACE_CHANNEL_TYPE_FILE_TRANSFER)
this._approveFileTransfer(account, conn, channel, dispatchOp, context);
else
Shell.decline_dispatch_op(context, 'Unsupported channel type');
},
_approveTextChannel: function(account, conn, channel, dispatchOp, context) {
@ -802,7 +815,7 @@ const ChatNotification = new Lang.Class({
let groups = this._contentArea.get_children();
for (let i = 0; i < groups.length; i++) {
let group = groups[i];
if (group.get_children().length == 0)
if (group.get_n_children() == 0)
group.destroy();
}
},

View File

@ -133,7 +133,7 @@ const IMStatusChooserItem = new Lang.Class({
item = new IMStatusItem(_("Busy"), 'user-busy');
this._combo.addMenuItem(item, IMStatus.BUSY);
item = new IMStatusItem(_("Hidden"), 'user-invisible');
item = new IMStatusItem(_("Invisible"), 'user-invisible');
this._combo.addMenuItem(item, IMStatus.HIDDEN);
item = new IMStatusItem(_("Away"), 'user-away');

View File

@ -1121,25 +1121,6 @@ const Workspace = new Lang.Class({
}
},
_showAllOverlays: function() {
let currentWorkspace = global.screen.get_active_workspace();
for (let i = 0; i < this._windows.length; i++) {
let clone = this._windows[i];
let overlay = this._windowOverlays[i];
this._showWindowOverlay(clone, overlay,
this.metaWorkspace == null || this.metaWorkspace == currentWorkspace);
}
},
_hideAllOverlays: function() {
for (let i = 0; i < this._windows.length; i++) {
let clone = this._windows[i];
let overlay = this._windowOverlays[i];
if (overlay)
overlay.hide();
}
},
_delayedWindowRepositioning: function() {
if (this._windowIsZooming)
return true;
@ -1339,6 +1320,10 @@ const Workspace = new Lang.Class({
// Position and scale the windows.
for (let i = 0; i < this._windows.length; i++) {
let clone = this._windows[i];
let overlay = this._windowOverlays[i];
if (overlay)
overlay.hide();
clone.zoomFromOverview();
@ -1363,8 +1348,6 @@ const Workspace = new Lang.Class({
});
}
}
this._hideAllOverlays();
},
destroy : function() {

View File

@ -1,6 +1,6 @@
data/gnome-shell.desktop.in.in
data/gnome-shell-extension-prefs.desktop.in.in
data/org.gnome.shell.gschema.xml.in
data/org.gnome.shell.gschema.xml.in.in
js/extensionPrefs/main.js
js/gdm/loginDialog.js
js/gdm/powerMenu.js
@ -13,6 +13,7 @@ js/ui/contactDisplay.js
js/ui/dash.js
js/ui/dateMenu.js
js/ui/endSessionDialog.js
js/ui/extensionDownloader.js
js/ui/extensionSystem.js
js/ui/keyboard.js
js/ui/keyringPrompt.js

550
po/ar.po

File diff suppressed because it is too large Load Diff

1007
po/el.po

File diff suppressed because it is too large Load Diff

443
po/es.po

File diff suppressed because it is too large Load Diff

472
po/gl.po

File diff suppressed because it is too large Load Diff

478
po/it.po

File diff suppressed because it is too large Load Diff

View File

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

247
po/nb.po
View File

@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gnome-shell 3.5.x\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-05-30 14:16+0200\n"
"PO-Revision-Date: 2012-05-30 14:19+0200\n"
"POT-Creation-Date: 2012-06-11 22:04+0200\n"
"PO-Revision-Date: 2012-06-11 22:04+0200\n"
"Last-Translator: Kjartan Maraas <kmaraas@gnome.org>\n"
"Language-Team: Norwegian bokmål <i18n-nb@lister.ping.uio.no>\n"
"Language: \n"
@ -35,11 +35,11 @@ msgstr "Brukervalg for GNOME Shell utvidelser"
msgid "Configure GNOME Shell Extensions"
msgstr "Konfigurer utvidelser for GNOME Shell"
#: ../data/org.gnome.shell.gschema.xml.in.h:1
#: ../data/org.gnome.shell.gschema.xml.in.in.h:1
msgid "Enable internal tools useful for developers and testers from Alt-F2"
msgstr "Aktiver interne verktøy for utviklere og testere fra Alt-F2"
#: ../data/org.gnome.shell.gschema.xml.in.h:2
#: ../data/org.gnome.shell.gschema.xml.in.in.h:2
msgid ""
"Allows access to internal debugging and monitoring tools using the Alt-F2 "
"dialog."
@ -47,11 +47,11 @@ msgstr ""
"Tillat tilgang til interne feilsøkings- og overvåkingsverktøy ved å bruke "
"Alt-F2-dialogen."
#: ../data/org.gnome.shell.gschema.xml.in.h:3
#: ../data/org.gnome.shell.gschema.xml.in.in.h:3
msgid "Uuids of extensions to enable"
msgstr "Uuider på utvidelser som skal slås på"
#: ../data/org.gnome.shell.gschema.xml.in.h:4
#: ../data/org.gnome.shell.gschema.xml.in.in.h:4
msgid ""
"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 "
@ -63,11 +63,11 @@ msgstr ""
"i denne listen. Du kan også manipulere denne listen med DBus-metodene "
"EnablExtension og DisableExtension på org.gnome.Shell."
#: ../data/org.gnome.shell.gschema.xml.in.h:5
#: ../data/org.gnome.shell.gschema.xml.in.in.h:5
msgid "Whether to collect stats about applications usage"
msgstr "Om det skal samles statistikk om bruk av programmer"
#: ../data/org.gnome.shell.gschema.xml.in.h:6
#: ../data/org.gnome.shell.gschema.xml.in.in.h:6
msgid ""
"The shell normally monitors active applications in order to present the most "
"used ones (e.g. in launchers). While this data will be kept private, you may "
@ -79,11 +79,11 @@ msgstr ""
"holdt privat, men du kan deaktivere denne lagringen av personvernårsaker. "
"Hvis du slår det av, vil det ikke fjerne allerede lagret informasjon."
#: ../data/org.gnome.shell.gschema.xml.in.h:7
#: ../data/org.gnome.shell.gschema.xml.in.in.h:7
msgid "List of desktop file IDs for favorite applications"
msgstr "Liste av skrivebordfil-ider for favorittprogrammer"
#: ../data/org.gnome.shell.gschema.xml.in.h:8
#: ../data/org.gnome.shell.gschema.xml.in.in.h:8
msgid ""
"The applications corresponding to these identifiers will be displayed in the "
"favorites area."
@ -91,83 +91,83 @@ msgstr ""
"Programmene som passer til disse identifikatorene vil bli vist i "
"favorittområdet."
#: ../data/org.gnome.shell.gschema.xml.in.h:9
#: ../data/org.gnome.shell.gschema.xml.in.in.h:9
msgid "disabled OpenSearch providers"
msgstr "OpenSearch tilbydere slått av"
#: ../data/org.gnome.shell.gschema.xml.in.h:10
#: ../data/org.gnome.shell.gschema.xml.in.in.h:10
msgid "History for command (Alt-F2) dialog"
msgstr "Historikk for kommandodialog (Alt-F2)"
#: ../data/org.gnome.shell.gschema.xml.in.h:11
#: ../data/org.gnome.shell.gschema.xml.in.in.h:11
msgid "History for the looking glass dialog"
msgstr "Historikk for forstørrelsesglass-dialogen"
#: ../data/org.gnome.shell.gschema.xml.in.h:12
#: ../data/org.gnome.shell.gschema.xml.in.in.h:12
msgid ""
"Internally used to store the last IM presence explicitly set by the user. "
"The value here is from the TpConnectionPresenceType enumeration."
msgstr ""
#: ../data/org.gnome.shell.gschema.xml.in.h:13
#: ../data/org.gnome.shell.gschema.xml.in.in.h:13
msgid ""
"Internally used to store the last session presence status for the user. The "
"value here is from the GsmPresenceStatus enumeration."
msgstr ""
#: ../data/org.gnome.shell.gschema.xml.in.h:14
#: ../data/org.gnome.shell.gschema.xml.in.in.h:14
msgid "Show the week date in the calendar"
msgstr "Vis dato for uken i kalender"
#: ../data/org.gnome.shell.gschema.xml.in.h:15
#: ../data/org.gnome.shell.gschema.xml.in.in.h:15
msgid "If true, display the ISO week date in the calendar."
msgstr "Viser ISO-ukedato i kalenderen hvis «true»."
#: ../data/org.gnome.shell.gschema.xml.in.h:16
#: ../data/org.gnome.shell.gschema.xml.in.in.h:16
msgid "Keybinding to open the application menu"
msgstr "Tastaturbinding som åpner programmenyen"
#: ../data/org.gnome.shell.gschema.xml.in.h:17
#: ../data/org.gnome.shell.gschema.xml.in.in.h:17
msgid "Keybinding to open the application menu."
msgstr "Tastaturbinding som åpner programmenyen."
#: ../data/org.gnome.shell.gschema.xml.in.h:18
#: ../data/org.gnome.shell.gschema.xml.in.in.h:18
msgid "Keybinding to toggle the screen recorder"
msgstr "Tastaturbinding som slår av/på skjermopptak"
#: ../data/org.gnome.shell.gschema.xml.in.h:19
#: ../data/org.gnome.shell.gschema.xml.in.in.h:19
msgid "Keybinding to start/stop the builtin screen recorder."
msgstr "Tastaturbinding som starter/stopper innebygget opptak av skjerm."
#: ../data/org.gnome.shell.gschema.xml.in.h:20
#: ../data/org.gnome.shell.gschema.xml.in.in.h:20
msgid "Which keyboard to use"
msgstr "Tastatur som skal brukes"
#: ../data/org.gnome.shell.gschema.xml.in.h:21
#: ../data/org.gnome.shell.gschema.xml.in.in.h:21
msgid "The type of keyboard to use."
msgstr "Type tastatur som skal brukes."
#: ../data/org.gnome.shell.gschema.xml.in.h:22
#: ../data/org.gnome.shell.gschema.xml.in.in.h:22
msgid "Show time with seconds"
msgstr "Vis tid med sekunder"
#: ../data/org.gnome.shell.gschema.xml.in.h:23
#: ../data/org.gnome.shell.gschema.xml.in.in.h:23
msgid "If true, display seconds in time."
msgstr "Viser sekunder i klokken hvis «true»."
#: ../data/org.gnome.shell.gschema.xml.in.h:24
#: ../data/org.gnome.shell.gschema.xml.in.in.h:24
msgid "Show date in clock"
msgstr "Vis dato i klokken"
#: ../data/org.gnome.shell.gschema.xml.in.h:25
#: ../data/org.gnome.shell.gschema.xml.in.in.h:25
msgid "If true, display date in the clock, in addition to time."
msgstr "Viser dato i tillegg til tid i klokken hvis «true»."
#: ../data/org.gnome.shell.gschema.xml.in.h:26
#: ../data/org.gnome.shell.gschema.xml.in.in.h:26
msgid "Framerate used for recording screencasts."
msgstr "Bildefrekvens i bruk ved lagring av skjermvideoer."
#: ../data/org.gnome.shell.gschema.xml.in.h:27
#: ../data/org.gnome.shell.gschema.xml.in.in.h:27
msgid ""
"The framerate of the resulting screencast recordered by GNOME Shell's "
"screencast recorder in frames-per-second."
@ -175,11 +175,11 @@ msgstr ""
"Bildefrekvensen i den ferdige skjermvideoen tatt opp med GNOME Shells "
"skjermvideoopptaker i bilder per sekund."
#: ../data/org.gnome.shell.gschema.xml.in.h:28
#: ../data/org.gnome.shell.gschema.xml.in.in.h:28
msgid "The gstreamer pipeline used to encode the screencast"
msgstr "Gstreamer-kommandokø brukt til å kode skjermvideoen"
#: ../data/org.gnome.shell.gschema.xml.in.h:30
#: ../data/org.gnome.shell.gschema.xml.in.in.h:30
#, fuzzy, no-c-format
msgid ""
"Sets the GStreamer pipeline used to encode recordings. It follows the syntax "
@ -196,11 +196,11 @@ msgstr ""
"Setter GStreamer-rør som brukes til å kode opptak. Den følger syntaksen som "
"brukes for gst-launch. Røret må ha en..."
#: ../data/org.gnome.shell.gschema.xml.in.h:31
#: ../data/org.gnome.shell.gschema.xml.in.in.h:31
msgid "File extension used for storing the screencast"
msgstr "Filendelse i bruk ved lagring av skjermvideo"
#: ../data/org.gnome.shell.gschema.xml.in.h:32
#: ../data/org.gnome.shell.gschema.xml.in.in.h:32
msgid ""
"The filename for recorded screencasts will be a unique filename based on the "
"current date, and use this extension. It should be changed when recording to "
@ -245,9 +245,9 @@ msgstr "(eller dra finger)"
msgid "Not listed?"
msgstr "Ikke listet?"
#: ../js/gdm/loginDialog.js:1023 ../js/ui/endSessionDialog.js:401
#: ../js/ui/extensionSystem.js:375 ../js/ui/networkAgent.js:153
#: ../js/ui/polkitAuthenticationAgent.js:175 ../js/ui/status/bluetooth.js:431
#: ../js/gdm/loginDialog.js:1023 ../js/ui/endSessionDialog.js:410
#: ../js/ui/extensionSystem.js:376 ../js/ui/networkAgent.js:153
#: ../js/ui/polkitAuthenticationAgent.js:176 ../js/ui/status/bluetooth.js:431
msgid "Cancel"
msgstr "Avbryt"
@ -260,8 +260,8 @@ msgstr "Logg inn"
msgid "Login Window"
msgstr "Innloggingsvindu"
#: ../js/gdm/powerMenu.js:130 ../js/ui/userMenu.js:595
#: ../js/ui/userMenu.js:599 ../js/ui/userMenu.js:647
#: ../js/gdm/powerMenu.js:130 ../js/ui/userMenu.js:604
#: ../js/ui/userMenu.js:608 ../js/ui/userMenu.js:696
msgid "Suspend"
msgstr "Hvilemodus"
@ -269,8 +269,8 @@ msgstr "Hvilemodus"
msgid "Restart"
msgstr "Start på nytt"
#: ../js/gdm/powerMenu.js:140 ../js/ui/userMenu.js:597
#: ../js/ui/userMenu.js:599 ../js/ui/userMenu.js:646
#: ../js/gdm/powerMenu.js:140 ../js/ui/userMenu.js:606
#: ../js/ui/userMenu.js:608 ../js/ui/userMenu.js:695
msgid "Power Off"
msgstr "Slå av"
@ -290,27 +290,27 @@ msgid "Execution of '%s' failed:"
msgstr "Kjøring av «%s» feilet:"
#. Translators: Filter to display all applications
#: ../js/ui/appDisplay.js:255
#: ../js/ui/appDisplay.js:251
msgid "All"
msgstr "Alle"
#: ../js/ui/appDisplay.js:314
#: ../js/ui/appDisplay.js:309
msgid "APPLICATIONS"
msgstr "PROGRAMMER"
#: ../js/ui/appDisplay.js:374
#: ../js/ui/appDisplay.js:369
msgid "SETTINGS"
msgstr "INNSTILLINGER"
#: ../js/ui/appDisplay.js:679
#: ../js/ui/appDisplay.js:674
msgid "New Window"
msgstr "Nytt vindu"
#: ../js/ui/appDisplay.js:682
#: ../js/ui/appDisplay.js:677
msgid "Remove from Favorites"
msgstr "Fjern fra favoritter"
#: ../js/ui/appDisplay.js:683
#: ../js/ui/appDisplay.js:678
msgid "Add to Favorites"
msgstr "Legg til i favoritter"
@ -483,8 +483,8 @@ msgstr "Denne uken"
msgid "Next week"
msgstr "Neste uke"
#: ../js/ui/contactDisplay.js:66 ../js/ui/notificationDaemon.js:486
#: ../js/ui/status/power.js:215 ../src/shell-app.c:374
#: ../js/ui/contactDisplay.js:66
msgctxt "contact"
msgid "Unknown"
msgstr "Ukjent"
@ -567,103 +567,103 @@ msgstr "%a %l.%M %p"
msgid "%A %B %e, %Y"
msgstr "%a %e %B, %Y"
#: ../js/ui/endSessionDialog.js:61
#: ../js/ui/endSessionDialog.js:60
#, c-format
msgctxt "title"
msgid "Log Out %s"
msgstr "Logg ut %s"
#: ../js/ui/endSessionDialog.js:62
#: ../js/ui/endSessionDialog.js:61
msgctxt "title"
msgid "Log Out"
msgstr "Logg ut"
#: ../js/ui/endSessionDialog.js:63
#: ../js/ui/endSessionDialog.js:62
msgid "Click Log Out to quit these applications and log out of the system."
msgstr ""
"Klikk Logg ut for å avslutte disse programmene og logge ut av systemet."
#: ../js/ui/endSessionDialog.js:65
#: ../js/ui/endSessionDialog.js:64
#, c-format
msgid "%s will be logged out automatically in %d second."
msgid_plural "%s will be logged out automatically in %d seconds."
msgstr[0] "%s vil bli logget ut automatisk om %d sekund."
msgstr[1] "%s vil bli logget ut automatisk om %d sekunder."
#: ../js/ui/endSessionDialog.js:70
#: ../js/ui/endSessionDialog.js:69
#, c-format
msgid "You will be logged out automatically in %d second."
msgid_plural "You will be logged out automatically in %d seconds."
msgstr[0] "Du vil bli logget ut automatisk om %d sekund."
msgstr[1] "Du vil bli logget ut automatisk om %d sekunder."
#: ../js/ui/endSessionDialog.js:74
#: ../js/ui/endSessionDialog.js:73
msgid "Logging out of the system."
msgstr "Logger ut av systemet"
#: ../js/ui/endSessionDialog.js:76
#: ../js/ui/endSessionDialog.js:75
msgctxt "button"
msgid "Log Out"
msgstr "Logg ut"
#: ../js/ui/endSessionDialog.js:81
#: ../js/ui/endSessionDialog.js:80
msgctxt "title"
msgid "Power Off"
msgstr "Slå av"
#: ../js/ui/endSessionDialog.js:82
#: ../js/ui/endSessionDialog.js:81
msgid "Click Power Off to quit these applications and power off the system."
msgstr ""
"Klikk «Slå av» for å avslutte disse programmene og logge ut av systemet."
#: ../js/ui/endSessionDialog.js:84
#: ../js/ui/endSessionDialog.js:83
#, c-format
msgid "The system will power off automatically in %d second."
msgid_plural "The system will power off automatically in %d seconds."
msgstr[0] "Systemet vil slås av automatisk om %d sekund."
msgstr[1] "Systemet vil slås av automatisk om %d sekunder."
#: ../js/ui/endSessionDialog.js:88
#: ../js/ui/endSessionDialog.js:87
msgid "Powering off the system."
msgstr "Slår av systemet."
#: ../js/ui/endSessionDialog.js:90 ../js/ui/endSessionDialog.js:107
#: ../js/ui/endSessionDialog.js:89 ../js/ui/endSessionDialog.js:106
msgctxt "button"
msgid "Restart"
msgstr "Start på nytt"
#: ../js/ui/endSessionDialog.js:92
#: ../js/ui/endSessionDialog.js:91
msgctxt "button"
msgid "Power Off"
msgstr "Slå av"
#: ../js/ui/endSessionDialog.js:98
#: ../js/ui/endSessionDialog.js:97
msgctxt "title"
msgid "Restart"
msgstr "Start på nytt"
#: ../js/ui/endSessionDialog.js:99
#: ../js/ui/endSessionDialog.js:98
msgid "Click Restart to quit these applications and restart the system."
msgstr ""
"Klikk Start på nytt for å avslutte disse programmene og starte systemet på "
"nytt."
#: ../js/ui/endSessionDialog.js:101
#: ../js/ui/endSessionDialog.js:100
#, c-format
msgid "The system will restart automatically in %d second."
msgid_plural "The system will restart automatically in %d seconds."
msgstr[0] "Systemet vil starte på nytt automatisk om %d sekund."
msgstr[1] "Systemet vil starte på nytt automatisk om %d sekunder."
#: ../js/ui/endSessionDialog.js:105
#: ../js/ui/endSessionDialog.js:104
msgid "Restarting the system."
msgstr "Starter systemet på nytt."
#: ../js/ui/extensionSystem.js:379
#: ../js/ui/extensionSystem.js:380
msgid "Install"
msgstr "Installer"
#: ../js/ui/extensionSystem.js:383
#: ../js/ui/extensionSystem.js:384
#, c-format
msgid "Download and install '%s' from extensions.gnome.org?"
msgstr "Last ned og installer «%s» fra extensions.gnome.org?"
@ -672,12 +672,12 @@ msgstr "Last ned og installer «%s» fra extensions.gnome.org?"
msgid "tray"
msgstr "varslingsområde"
#: ../js/ui/keyboard.js:545 ../js/ui/status/keyboard.js:44
#: ../js/ui/keyboard.js:545 ../js/ui/status/keyboard.js:42
#: ../js/ui/status/power.js:203
msgid "Keyboard"
msgstr "Tastatur"
#: ../js/ui/keyringPrompt.js:85 ../js/ui/polkitAuthenticationAgent.js:272
#: ../js/ui/keyringPrompt.js:85 ../js/ui/polkitAuthenticationAgent.js:273
msgid "Password:"
msgstr "Passord:"
@ -734,7 +734,7 @@ msgid "Web Page"
msgstr "Nettside"
#. Translators: this is a filename used for screencast recording
#: ../js/ui/main.js:121
#: ../js/ui/main.js:126
#, no-c-format
msgid "Screencast from %d %t"
msgstr "Skjermvideo fra %d %t"
@ -751,7 +751,7 @@ msgstr "Fjern demping"
msgid "Mute"
msgstr "Demp"
#: ../js/ui/messageTray.js:2522
#: ../js/ui/messageTray.js:2528
msgid "System Information"
msgstr "Systeminformasjon"
@ -836,25 +836,30 @@ msgstr "Nettverkspassord for mobilt bredbånd"
msgid "A password is required to connect to '%s'."
msgstr "Et passord kreves for å koble til «%s»."
#: ../js/ui/overview.js:90
#: ../js/ui/notificationDaemon.js:486 ../src/shell-app.c:374
msgctxt "program"
msgid "Unknown"
msgstr "Ukjent"
#: ../js/ui/overview.js:89
msgid "Undo"
msgstr "Angre"
#: ../js/ui/overview.js:130
#: ../js/ui/overview.js:129
msgid "Overview"
msgstr "Oversikt"
#: ../js/ui/overview.js:200
#: ../js/ui/overview.js:199
msgid "Windows"
msgstr "Vinduer"
#: ../js/ui/overview.js:203
#: ../js/ui/overview.js:202
msgid "Applications"
msgstr "Programmer"
#. Translators: this is the name of the dock/favorites area on
#. the left of the overview
#: ../js/ui/overview.js:229
#: ../js/ui/overview.js:228
msgid "Dash"
msgstr "Favoritter"
@ -897,7 +902,7 @@ msgstr "Autentisering kreves"
msgid "Administrator"
msgstr "Administrator"
#: ../js/ui/polkitAuthenticationAgent.js:179
#: ../js/ui/polkitAuthenticationAgent.js:180
msgid "Authenticate"
msgstr "Autentiser"
@ -905,7 +910,7 @@ msgstr "Autentiser"
#. * requested authentication was not gained; this can happen
#. * because of an authentication error (like invalid password),
#. * for instance.
#: ../js/ui/polkitAuthenticationAgent.js:260
#: ../js/ui/polkitAuthenticationAgent.js:261
msgid "Sorry, that didn't work. Please try again."
msgstr "Beklager, det virket ikke. Vennligst prøv igjen."
@ -922,11 +927,11 @@ msgstr "toggle-switch-intl"
msgid "Please enter a command:"
msgstr "Oppgi en kommando:"
#: ../js/ui/searchDisplay.js:321
#: ../js/ui/searchDisplay.js:323
msgid "Searching..."
msgstr "Søker …"
#: ../js/ui/searchDisplay.js:374
#: ../js/ui/searchDisplay.js:376
msgid "No matching results."
msgstr "Ingen treff."
@ -1000,7 +1005,7 @@ msgstr "Stor tekst"
#: ../js/ui/status/bluetooth.js:31 ../js/ui/status/bluetooth.js:35
#: ../js/ui/status/bluetooth.js:255 ../js/ui/status/bluetooth.js:308
#: ../js/ui/status/bluetooth.js:339 ../js/ui/status/bluetooth.js:375
#: ../js/ui/status/bluetooth.js:404 ../js/ui/status/network.js:890
#: ../js/ui/status/bluetooth.js:404 ../js/ui/status/network.js:889
msgid "Bluetooth"
msgstr "Bluetooth"
@ -1029,11 +1034,12 @@ msgstr "maskinvare slått av"
msgid "Connection"
msgstr "Tilkobling"
#: ../js/ui/status/bluetooth.js:211 ../js/ui/status/network.js:491
#: ../js/ui/status/bluetooth.js:211 ../js/ui/status/network.js:490
msgid "disconnecting..."
msgstr "kobler fra …"
#: ../js/ui/status/bluetooth.js:224 ../js/ui/status/network.js:497
#: ../js/ui/status/bluetooth.js:224 ../js/ui/status/network.js:496
#: ../js/ui/status/network.js:956
msgid "connecting..."
msgstr "kobler til …"
@ -1124,11 +1130,11 @@ msgstr "Vennligst oppgi PIN som oppgitt på enheten."
msgid "OK"
msgstr "OK"
#: ../js/ui/status/keyboard.js:72
#: ../js/ui/status/keyboard.js:69
msgid "Show Keyboard Layout"
msgstr "Vis tastaturutforming"
#: ../js/ui/status/keyboard.js:77
#: ../js/ui/status/keyboard.js:71
msgid "Region and Language Settings"
msgstr "Innstillinger for region og språk"
@ -1143,113 +1149,113 @@ msgstr "slått av"
#. Translators: this is for network devices that are physically present but are not
#. under NetworkManager's control (and thus cannot be used in the menu)
#: ../js/ui/status/network.js:489
#: ../js/ui/status/network.js:488
msgid "unmanaged"
msgstr "ikke håndtert"
#. Translators: this is for network connections that require some kind of key or password
#: ../js/ui/status/network.js:500
#: ../js/ui/status/network.js:499 ../js/ui/status/network.js:959
msgid "authentication required"
msgstr "autentisering kreves"
#. Translators: this is for devices that require some kind of firmware or kernel
#. module, which is missing
#: ../js/ui/status/network.js:510
#: ../js/ui/status/network.js:509
msgid "firmware missing"
msgstr "fastvare mangler"
#. Translators: this is for wired network devices that are physically disconnected
#: ../js/ui/status/network.js:517
#: ../js/ui/status/network.js:516
msgid "cable unplugged"
msgstr "kabel koblet fra"
#. Translators: this is for a network device that cannot be activated (for example it
#. is disabled by rfkill, or it has no coverage
#: ../js/ui/status/network.js:522
#: ../js/ui/status/network.js:521
msgid "unavailable"
msgstr "ikke tilgjengelig"
#: ../js/ui/status/network.js:524
#: ../js/ui/status/network.js:523 ../js/ui/status/network.js:961
msgid "connection failed"
msgstr "tilkobling feilet"
#: ../js/ui/status/network.js:585 ../js/ui/status/network.js:1497
#: ../js/ui/status/network.js:584 ../js/ui/status/network.js:1529
msgid "More..."
msgstr "Mer …"
#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
#. and we cannot access its settings (including the name)
#: ../js/ui/status/network.js:621 ../js/ui/status/network.js:1432
#: ../js/ui/status/network.js:620 ../js/ui/status/network.js:1459
msgid "Connected (private)"
msgstr "Tilkoblet (privat)"
#: ../js/ui/status/network.js:696
#: ../js/ui/status/network.js:695
msgid "Auto Ethernet"
msgstr "Automatisk Ethernet"
#: ../js/ui/status/network.js:754
#: ../js/ui/status/network.js:753
msgid "Auto broadband"
msgstr "Automatisk bredbånd"
#: ../js/ui/status/network.js:757
#: ../js/ui/status/network.js:756
msgid "Auto dial-up"
msgstr "Automatisk oppringt"
#. TRANSLATORS: this the automatic wireless connection name (including the network name)
#: ../js/ui/status/network.js:876 ../js/ui/status/network.js:1444
#: ../js/ui/status/network.js:875 ../js/ui/status/network.js:1476
#, c-format
msgid "Auto %s"
msgstr "Automatisk %s"
#: ../js/ui/status/network.js:878
#: ../js/ui/status/network.js:877
msgid "Auto bluetooth"
msgstr "Automatisk Bluetooth"
#: ../js/ui/status/network.js:1446
#: ../js/ui/status/network.js:1478
msgid "Auto wireless"
msgstr "Automatisk trådløst"
#: ../js/ui/status/network.js:1533
#: ../js/ui/status/network.js:1565
msgid "Network"
msgstr "Nettverk"
#: ../js/ui/status/network.js:1540
#: ../js/ui/status/network.js:1572
msgid "Enable networking"
msgstr "Slå på nettverk"
#: ../js/ui/status/network.js:1552
#: ../js/ui/status/network.js:1593
msgid "Wired"
msgstr "Kablet"
#: ../js/ui/status/network.js:1563
#: ../js/ui/status/network.js:1604
msgid "Wireless"
msgstr "Trådløst"
#: ../js/ui/status/network.js:1573
#: ../js/ui/status/network.js:1614
msgid "Mobile broadband"
msgstr "Mobilt bredbånd"
#: ../js/ui/status/network.js:1583
#: ../js/ui/status/network.js:1624
msgid "VPN Connections"
msgstr "VPN-tilkoblinger"
#: ../js/ui/status/network.js:1594
#: ../js/ui/status/network.js:1631
msgid "Network Settings"
msgstr "Innstillinger for nettverk"
#: ../js/ui/status/network.js:1647
#: ../js/ui/status/network.js:1675
msgid "Network Manager"
msgstr "Nettverkshåndtering"
#: ../js/ui/status/network.js:1734
#: ../js/ui/status/network.js:1768
msgid "Connection failed"
msgstr "Tilkobling feilet"
#: ../js/ui/status/network.js:1735
#: ../js/ui/status/network.js:1769
msgid "Activation of network connection failed"
msgstr "Aktivering av nettverkstilkobling feilet"
#: ../js/ui/status/network.js:1988
#: ../js/ui/status/network.js:2029
msgid "Networking is disabled"
msgstr "Nettverk er slått av"
@ -1345,6 +1351,11 @@ msgstr "Nettbrett"
msgid "Computer"
msgstr "Datamaskin"
#: ../js/ui/status/power.js:215
msgctxt "device"
msgid "Unknown"
msgstr "Ukjent"
#. Translators: This is the label for audio volume
#: ../js/ui/status/volume.js:25 ../js/ui/status/volume.js:39
msgid "Volume"
@ -1620,35 +1631,35 @@ msgstr "Ledig"
msgid "Unavailable"
msgstr "Ikke tilgjengelig"
#: ../js/ui/userMenu.js:556 ../js/ui/userMenu.js:656
#: ../js/ui/userMenu.js:565 ../js/ui/userMenu.js:705
msgid "Switch User"
msgstr "Bytt bruker"
#: ../js/ui/userMenu.js:557
#: ../js/ui/userMenu.js:566
msgid "Switch Session"
msgstr "Bytt økt"
#: ../js/ui/userMenu.js:632
#: ../js/ui/userMenu.js:681
msgid "Notifications"
msgstr "Varslinger"
#: ../js/ui/userMenu.js:641
#: ../js/ui/userMenu.js:690
msgid "System Settings"
msgstr "Systeminnstillinger"
#: ../js/ui/userMenu.js:661
#: ../js/ui/userMenu.js:710
msgid "Log Out"
msgstr "Logg ut"
#: ../js/ui/userMenu.js:669
#: ../js/ui/userMenu.js:718
msgid "Lock"
msgstr "Lås"
#: ../js/ui/userMenu.js:687
#: ../js/ui/userMenu.js:736
msgid "Your chat status will be set to busy"
msgstr "Din pratestatus vil bli satt til opptatt"
#: ../js/ui/userMenu.js:688
#: ../js/ui/userMenu.js:737
msgid ""
"Notifications are now disabled, including chat messages. Your online status "
"has been adjusted to let others know that you might not see their messages."

717
po/pa.po

File diff suppressed because it is too large Load Diff

707
po/ru.po

File diff suppressed because it is too large Load Diff

427
po/sl.po

File diff suppressed because it is too large Load Diff

View File

@ -109,7 +109,6 @@ shell_public_headers_h = \
shell-app.h \
shell-app-system.h \
shell-app-usage.h \
shell-contact-system.h \
shell-embedded-window.h \
shell-generic-container.h \
shell-gtk-embed.h \
@ -132,6 +131,10 @@ shell_public_headers_h = \
shell-wm.h \
shell-xfixes-cursor.h
if BUILD_WITH_FOLKS
shell_public_headers_h += shell-contact-system.h
endif
shell_private_sources = \
gactionmuxer.h \
gactionmuxer.c \
@ -153,11 +156,8 @@ libgnome_shell_la_SOURCES = \
shell-wm-private.h \
gnome-shell-plugin.c \
shell-app.c \
shell-a11y.h \
shell-a11y.c \
shell-app-system.c \
shell-app-usage.c \
shell-contact-system.c \
shell-embedded-window.c \
shell-generic-container.c \
shell-gtk-embed.c \
@ -187,6 +187,9 @@ libgnome_shell_la_SOURCES = \
shell-xfixes-cursor.c \
$(NULL)
if BUILD_WITH_FOLKS
libgnome_shell_la_SOURCES += shell-contact-system.c
endif
libgnome_shell_la_gir_sources = \
$(filter-out %-private.h $(shell_private_sources), $(shell_public_headers_h) $(libgnome_shell_la_SOURCES))
@ -303,7 +306,10 @@ libgnome_shell_la_LIBADD = \
libgnome_shell_la_CPPFLAGS = $(gnome_shell_cflags)
Shell-0.1.gir: libgnome-shell.la St-1.0.gir
Shell_0_1_gir_INCLUDES = Clutter-1.0 ClutterX11-1.0 Meta-3.0 TelepathyGLib-0.12 TelepathyLogger-0.2 Soup-2.4 GMenu-3.0 NetworkManager-1.0 NMClient-1.0 Folks-0.6
Shell_0_1_gir_INCLUDES = Clutter-1.0 ClutterX11-1.0 Meta-3.0 TelepathyGLib-0.12 TelepathyLogger-0.2 Soup-2.4 GMenu-3.0 NetworkManager-1.0 NMClient-1.0
if BUILD_WITH_FOLKS
Shell_0_1_gir_INCLUDES += Folks-0.6
endif
Shell_0_1_gir_CFLAGS = $(libgnome_shell_la_CPPFLAGS) -I $(srcdir)
Shell_0_1_gir_LIBS = libgnome-shell.la
Shell_0_1_gir_FILES = $(libgnome_shell_la_gir_sources)

View File

@ -29,11 +29,9 @@
#include <libintl.h>
#include <string.h>
#include <gconf/gconf-client.h>
#define HANDLE_LIBICAL_MEMORY
#include <libecal/e-cal-client.h>
#include <libedataserver/e-source-list.h>
#include <libedataserverui/e-client-utils.h>
#include <libecal/libecal.h>
#include <libedataserverui/libedataserverui.h>
#undef CALENDAR_ENABLE_DEBUG
#include "calendar-debug.h"
@ -48,30 +46,23 @@
#define CALENDAR_SOURCES_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CALENDAR_TYPE_SOURCES, CalendarSourcesPrivate))
#define CALENDAR_SOURCES_EVO_DIR "/apps/evolution"
#define CALENDAR_SOURCES_APPOINTMENT_SOURCES_KEY CALENDAR_SOURCES_EVO_DIR "/calendar/sources"
#define CALENDAR_SOURCES_TASK_SOURCES_KEY CALENDAR_SOURCES_EVO_DIR "/tasks/sources"
/* org.gnome.shell.evolution.calendar has the same data behind it
* as org.gnome.evolution.calendar, but is a small schema we install
* ourselves */
#define CALENDAR_SELECTED_SOURCES_SCHEMA "org.gnome.shell.evolution.calendar"
#define CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_KEY "selected-calendars"
#define CALENDAR_SOURCES_SELECTED_TASK_SOURCES_KEY "selected-tasks"
typedef struct _ClientData ClientData;
typedef struct _CalendarSourceData CalendarSourceData;
struct _ClientData
{
ECalClient *client;
gulong backend_died_id;
};
struct _CalendarSourceData
{
ECalClientSourceType source_type;
CalendarSources *sources;
guint changed_signal;
GSList *clients;
char **selected_sources;
ESourceList *esource_list;
guint selected_sources_handler_id;
/* ESource -> EClient */
GHashTable *clients;
guint timeout_id;
@ -80,11 +71,13 @@ struct _CalendarSourceData
struct _CalendarSourcesPrivate
{
ESourceRegistry *registry;
gulong source_added_id;
gulong source_changed_id;
gulong source_removed_id;
CalendarSourceData appointment_sources;
CalendarSourceData task_sources;
GConfClient *gconf_client;
GSettings *settings;
};
static void calendar_sources_class_init (CalendarSourcesClass *klass);
@ -92,8 +85,12 @@ static void calendar_sources_init (CalendarSources *sources);
static void calendar_sources_finalize (GObject *object);
static void backend_died_cb (EClient *client, CalendarSourceData *source_data);
static void calendar_sources_esource_list_changed (ESourceList *source_list,
CalendarSourceData *source_data);
static void calendar_sources_registry_source_changed_cb (ESourceRegistry *registry,
ESource *source,
CalendarSources *sources);
static void calendar_sources_registry_source_removed_cb (ESourceRegistry *registry,
ESource *source,
CalendarSources *sources);
enum
{
@ -106,6 +103,14 @@ static guint signals [LAST_SIGNAL] = { 0, };
static GObjectClass *parent_class = NULL;
static CalendarSources *calendar_sources_singleton = NULL;
static void
client_data_free (ClientData *data)
{
g_signal_handler_disconnect (data->client, data->backend_died_id);
g_object_unref (data->client);
g_slice_free (ClientData, data);
}
GType
calendar_sources_get_type (void)
{
@ -173,20 +178,49 @@ calendar_sources_class_init (CalendarSourcesClass *klass)
static void
calendar_sources_init (CalendarSources *sources)
{
GError *error = NULL;
sources->priv = CALENDAR_SOURCES_GET_PRIVATE (sources);
/* XXX Not sure what to do if this fails.
* Should this class implement GInitable or pass the
* registry in as a G_PARAM_CONSTRUCT_ONLY property? */
sources->priv->registry = e_source_registry_new_sync (NULL, &error);
if (error != NULL)
{
g_error ("%s: %s", G_STRFUNC, error->message);
}
sources->priv->source_added_id = g_signal_connect (sources->priv->registry,
"source-added",
G_CALLBACK (calendar_sources_registry_source_changed_cb),
sources);
sources->priv->source_changed_id = g_signal_connect (sources->priv->registry,
"source-changed",
G_CALLBACK (calendar_sources_registry_source_changed_cb),
sources);
sources->priv->source_removed_id = g_signal_connect (sources->priv->registry,
"source-removed",
G_CALLBACK (calendar_sources_registry_source_removed_cb),
sources);
sources->priv->appointment_sources.source_type = E_CAL_CLIENT_SOURCE_TYPE_EVENTS;
sources->priv->appointment_sources.sources = sources;
sources->priv->appointment_sources.changed_signal = signals [APPOINTMENT_SOURCES_CHANGED];
sources->priv->appointment_sources.clients = g_hash_table_new_full ((GHashFunc) e_source_hash,
(GEqualFunc) e_source_equal,
(GDestroyNotify) g_object_unref,
(GDestroyNotify) client_data_free);
sources->priv->appointment_sources.timeout_id = 0;
sources->priv->task_sources.source_type = E_CAL_CLIENT_SOURCE_TYPE_TASKS;
sources->priv->task_sources.sources = sources;
sources->priv->task_sources.changed_signal = signals [TASK_SOURCES_CHANGED];
sources->priv->task_sources.clients = g_hash_table_new_full ((GHashFunc) e_source_hash,
(GEqualFunc) e_source_equal,
(GDestroyNotify) g_object_unref,
(GDestroyNotify) client_data_free);
sources->priv->task_sources.timeout_id = 0;
sources->priv->gconf_client = gconf_client_get_default ();
sources->priv->settings = g_settings_new (CALENDAR_SELECTED_SOURCES_SCHEMA);
}
static void
@ -195,37 +229,9 @@ calendar_sources_finalize_source_data (CalendarSources *sources,
{
if (source_data->loaded)
{
GSList *l;
if (source_data->selected_sources_handler_id)
{
g_signal_handler_disconnect (sources->priv->settings,
source_data->selected_sources_handler_id);
source_data->selected_sources_handler_id = 0;
}
for (l = source_data->clients; l; l = l->next)
{
g_signal_handlers_disconnect_by_func (G_OBJECT (l->data),
G_CALLBACK (backend_died_cb),
source_data);
g_object_unref (l->data);
}
g_slist_free (source_data->clients);
g_hash_table_destroy (source_data->clients);
source_data->clients = NULL;
if (source_data->esource_list)
{
g_signal_handlers_disconnect_by_func (source_data->esource_list,
G_CALLBACK (calendar_sources_esource_list_changed),
source_data);
g_object_unref (source_data->esource_list);
}
source_data->esource_list = NULL;
g_strfreev (source_data->selected_sources);
source_data->selected_sources = NULL;
if (source_data->timeout_id != 0)
{
g_source_remove (source_data->timeout_id);
@ -241,17 +247,21 @@ calendar_sources_finalize (GObject *object)
{
CalendarSources *sources = CALENDAR_SOURCES (object);
if (sources->priv->registry)
{
g_signal_handler_disconnect (sources->priv->registry,
sources->priv->source_added_id);
g_signal_handler_disconnect (sources->priv->registry,
sources->priv->source_changed_id);
g_signal_handler_disconnect (sources->priv->registry,
sources->priv->source_removed_id);
g_object_unref (sources->priv->registry);
}
sources->priv->registry = NULL;
calendar_sources_finalize_source_data (sources, &sources->priv->appointment_sources);
calendar_sources_finalize_source_data (sources, &sources->priv->task_sources);
if (sources->priv->gconf_client)
g_object_unref (sources->priv->gconf_client);
sources->priv->gconf_client = NULL;
if (sources->priv->settings)
g_object_unref (sources->priv->settings);
sources->priv->settings = NULL;
if (G_OBJECT_CLASS (parent_class)->finalize)
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@ -271,133 +281,72 @@ calendar_sources_get (void)
return calendar_sources_singleton;
}
static gboolean
is_source_selected (ESource *esource,
char **selected_sources)
{
const char *uid;
char **source;
uid = e_source_peek_uid (esource);
for (source = selected_sources; *source; source++)
{
if (!strcmp (*source, uid))
return TRUE;
}
return FALSE;
}
/* The clients are just created here but not loaded */
static ECalClient *
get_ecal_from_source (ESource *esource,
ECalClientSourceType source_type,
GSList *existing_clients)
static void
create_client_for_source (ESource *source,
ECalClientSourceType source_type,
CalendarSourceData *source_data)
{
ECalClient *retval;
ClientData *data;
ECalClient *client;
GError *error = NULL;
if (existing_clients)
client = g_hash_table_lookup (source_data->clients, source);
g_return_if_fail (client == NULL);
client = e_cal_client_new (source, source_type, &error);
if (!client)
{
GSList *l;
for (l = existing_clients; l; l = l->next)
{
EClient *client = E_CLIENT (l->data);
if (e_source_equal (esource, e_client_get_source (client)))
{
dprintf (" load_esource: found existing source ... returning that\n");
return g_object_ref (client);
}
}
}
retval = e_cal_client_new (esource, source_type, &error);
if (!retval)
{
g_warning ("Could not load source '%s' from '%s': %s",
e_source_peek_name (esource),
e_source_peek_relative_uri (esource),
g_warning ("Could not load source '%s': %s",
e_source_get_uid (source),
error->message);
g_clear_error(&error);
return NULL;
return;
}
g_signal_connect (retval, "authenticate",
G_CALLBACK (e_client_utils_authenticate_handler), NULL);
data = g_slice_new0 (ClientData);
data->client = client; /* takes ownership */
data->backend_died_id = g_signal_connect (client,
"backend-died",
G_CALLBACK (backend_died_cb),
source_data);
return retval;
}
/* - Order doesn't matter
* - Can just compare object pointers since we
* re-use client connections
*/
static gboolean
compare_ecal_lists (GSList *a,
GSList *b)
{
GSList *l;
if (g_slist_length (a) != g_slist_length (b))
return FALSE;
for (l = a; l; l = l->next)
{
if (!g_slist_find (b, l->data))
return FALSE;
}
return TRUE;
g_hash_table_insert (source_data->clients, g_object_ref (source), data);
}
static inline void
debug_dump_selected_sources (char **selected_sources)
debug_dump_ecal_list (GHashTable *clients)
{
#ifdef CALENDAR_ENABLE_DEBUG
char **source;
dprintf ("Selected sources:\n");
for (source = selected_sources; *source; source++)
{
dprintf (" %s\n", *source);
}
dprintf ("\n");
#endif
}
static inline void
debug_dump_ecal_list (GSList *ecal_list)
{
#ifdef CALENDAR_ENABLE_DEBUG
GSList *l;
GList *list, *link;
dprintf ("Loaded clients:\n");
for (l = ecal_list; l; l = l->next)
list = g_hash_table_get_keys (clients);
for (link = list; link != NULL; link = g_list_next (link))
{
EClient *client = l->data;
ESource *source = e_client_get_source (client);
ESource *source = E_SOURCE (link->data);
dprintf (" %s %s %s\n",
e_source_peek_uid (source),
e_source_peek_name (source),
e_client_get_uri (client));
dprintf (" %s %s\n",
e_source_get_uid (source),
e_source_get_display_name (source));
}
g_list_free (list);
#endif
}
static void
calendar_sources_load_esource_list (CalendarSourceData *source_data);
calendar_sources_load_esource_list (ESourceRegistry *registry,
CalendarSourceData *source_data);
static gboolean
backend_restart (gpointer data)
{
CalendarSourceData *source_data = data;
ESourceRegistry *registry;
calendar_sources_load_esource_list (source_data);
registry = source_data->sources->priv->registry;
calendar_sources_load_esource_list (registry, source_data);
g_signal_emit (source_data->sources, source_data->changed_signal, 0);
source_data->timeout_id = 0;
@ -407,16 +356,13 @@ backend_restart (gpointer data)
static void
backend_died_cb (EClient *client, CalendarSourceData *source_data)
{
const char *uristr;
ESource *source;
const char *display_name;
source_data->clients = g_slist_remove (source_data->clients, client);
if (g_slist_length (source_data->clients) < 1)
{
g_slist_free (source_data->clients);
source_data->clients = NULL;
}
uristr = e_client_get_uri (client);
g_warning ("The calendar backend for %s has crashed.", uristr);
source = e_client_get_source (client);
display_name = e_source_get_display_name (source);
g_warning ("The calendar backend for '%s' has crashed.", display_name);
g_hash_table_remove (source_data->clients, source);
if (source_data->timeout_id != 0)
{
@ -429,169 +375,162 @@ backend_died_cb (EClient *client, CalendarSourceData *source_data)
}
static void
calendar_sources_load_esource_list (CalendarSourceData *source_data)
calendar_sources_load_esource_list (ESourceRegistry *registry,
CalendarSourceData *source_data)
{
GSList *clients = NULL;
GSList *groups, *l;
gboolean emit_signal = FALSE;
GList *list, *link;
const gchar *extension_name;
g_return_if_fail (source_data->esource_list != NULL);
debug_dump_selected_sources (source_data->selected_sources);
dprintf ("Source groups:\n");
groups = e_source_list_peek_groups (source_data->esource_list);
for (l = groups; l; l = l->next)
switch (source_data->source_type)
{
GSList *esources, *s;
dprintf (" %s\n", e_source_group_peek_uid (l->data));
dprintf (" sources:\n");
esources = e_source_group_peek_sources (l->data);
for (s = esources; s; s = s->next)
{
ESource *esource = E_SOURCE (s->data);
ECalClient *client;
dprintf (" type = '%s' uid = '%s', name = '%s', relative uri = '%s': \n",
source_data->source_type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS ? "appointment" : "task",
e_source_peek_uid (esource),
e_source_peek_name (esource),
e_source_peek_relative_uri (esource));
if (is_source_selected (esource, source_data->selected_sources) &&
(client = get_ecal_from_source (esource, source_data->source_type, source_data->clients)))
{
clients = g_slist_prepend (clients, client);
}
}
}
dprintf ("\n");
if (source_data->loaded &&
!compare_ecal_lists (source_data->clients, clients))
emit_signal = TRUE;
for (l = source_data->clients; l; l = l->next)
{
g_signal_handlers_disconnect_by_func (G_OBJECT (l->data),
G_CALLBACK (backend_died_cb),
source_data);
g_object_unref (l->data);
}
g_slist_free (source_data->clients);
source_data->clients = g_slist_reverse (clients);
/* connect to backend_died after we disconnected the previous signal
* handlers. If we do it before, we'll lose some handlers (for clients that
* were already there before) */
for (l = source_data->clients; l; l = l->next)
{
g_signal_connect (G_OBJECT (l->data), "backend-died",
G_CALLBACK (backend_died_cb), source_data);
case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
extension_name = E_SOURCE_EXTENSION_CALENDAR;
break;
case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
extension_name = E_SOURCE_EXTENSION_TASK_LIST;
break;
default:
g_return_if_reached ();
}
if (emit_signal)
list = e_source_registry_list_sources (registry, extension_name);
for (link = list; link != NULL; link = g_list_next (link))
{
dprintf ("Emitting %s-sources-changed signal\n",
source_data->source_type == E_CAL_CLIENT_SOURCE_TYPE_EVENTS ? "appointment" : "task");
g_signal_emit (source_data->sources, source_data->changed_signal, 0);
ESource *source = E_SOURCE (link->data);
ESourceSelectable *extension;
gboolean show_source;
extension = e_source_get_extension (source, extension_name);
show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension);
if (show_source)
create_client_for_source (source, source_data->source_type, source_data);
}
debug_dump_ecal_list (source_data->clients);
g_list_free_full (list, g_object_unref);
}
static void
calendar_sources_esource_list_changed (ESourceList *source_list,
CalendarSourceData *source_data)
calendar_sources_registry_source_changed_cb (ESourceRegistry *registry,
ESource *source,
CalendarSources *sources)
{
dprintf ("ESourceList changed, reloading\n");
if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
{
CalendarSourceData *source_data;
ESourceSelectable *extension;
gboolean have_client;
gboolean show_source;
calendar_sources_load_esource_list (source_data);
source_data = &sources->priv->appointment_sources;
extension = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR);
have_client = (g_hash_table_lookup (source_data->clients, source) != NULL);
show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension);
if (!show_source && have_client)
{
g_hash_table_remove (source_data->clients, source);
g_signal_emit (sources, source_data->changed_signal, 0);
}
if (show_source && !have_client)
{
create_client_for_source (source, source_data->source_type, source_data);
g_signal_emit (sources, source_data->changed_signal, 0);
}
}
if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
{
CalendarSourceData *source_data;
ESourceSelectable *extension;
gboolean have_client;
gboolean show_source;
source_data = &sources->priv->task_sources;
extension = e_source_get_extension (source, E_SOURCE_EXTENSION_TASK_LIST);
have_client = (g_hash_table_lookup (source_data->clients, source) != NULL);
show_source = e_source_get_enabled (source) && e_source_selectable_get_selected (extension);
if (!show_source && have_client)
{
g_hash_table_remove (source_data->clients, source);
g_signal_emit (sources, source_data->changed_signal, 0);
}
if (show_source && !have_client)
{
create_client_for_source (source, source_data->source_type, source_data);
g_signal_emit (sources, source_data->changed_signal, 0);
}
}
}
static void
calendar_sources_selected_sources_notify (GSettings *settings,
const gchar *key,
CalendarSourceData *source_data)
calendar_sources_registry_source_removed_cb (ESourceRegistry *registry,
ESource *source,
CalendarSources *sources)
{
dprintf ("Selected sources key (%s) changed, reloading\n", key);
if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR))
{
CalendarSourceData *source_data;
g_strfreev (source_data->selected_sources);
source_data->selected_sources = g_settings_get_strv (settings, key);
source_data = &sources->priv->appointment_sources;
g_hash_table_remove (source_data->clients, source);
g_signal_emit (sources, source_data->changed_signal, 0);
}
calendar_sources_load_esource_list (source_data);
if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST))
{
CalendarSourceData *source_data;
source_data = &sources->priv->task_sources;
g_hash_table_remove (source_data->clients, source);
g_signal_emit (sources, source_data->changed_signal, 0);
}
}
static void
calendar_sources_load_sources (CalendarSources *sources,
CalendarSourceData *source_data,
const char *sources_key,
const char *selected_sources_key)
GList *
calendar_sources_get_appointment_clients (CalendarSources *sources)
{
GConfClient *gconf_client;
GSettings *settings;
char *signal_name;
GList *list, *link;
dprintf ("---------------------------\n");
dprintf ("Loading sources:\n");
dprintf (" sources_key: %s\n", sources_key);
dprintf (" selected_sources_key: %s\n", selected_sources_key);
gconf_client = sources->priv->gconf_client;
settings = sources->priv->settings;
source_data->selected_sources = g_settings_get_strv (settings, selected_sources_key);
signal_name = g_strconcat ("changed::", selected_sources_key, NULL);
source_data->selected_sources_handler_id =
g_signal_connect (settings, signal_name,
G_CALLBACK (calendar_sources_selected_sources_notify), source_data);
g_free (signal_name);
source_data->esource_list = e_source_list_new_for_gconf (gconf_client, sources_key);
g_signal_connect (source_data->esource_list, "changed",
G_CALLBACK (calendar_sources_esource_list_changed),
source_data);
calendar_sources_load_esource_list (source_data);
source_data->loaded = TRUE;
dprintf ("---------------------------\n");
}
GSList *
calendar_sources_get_appointment_sources (CalendarSources *sources)
{
g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
if (!sources->priv->appointment_sources.loaded)
{
calendar_sources_load_sources (sources,
&sources->priv->appointment_sources,
CALENDAR_SOURCES_APPOINTMENT_SOURCES_KEY,
CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_KEY);
calendar_sources_load_esource_list (sources->priv->registry,
&sources->priv->appointment_sources);
sources->priv->appointment_sources.loaded = TRUE;
}
return sources->priv->appointment_sources.clients;
list = g_hash_table_get_values (sources->priv->appointment_sources.clients);
for (link = list; link != NULL; link = g_list_next (link))
link->data = ((ClientData *) link->data)->client;
return list;
}
GSList *
calendar_sources_get_task_sources (CalendarSources *sources)
GList *
calendar_sources_get_task_clients (CalendarSources *sources)
{
GList *list, *link;
g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
if (!sources->priv->task_sources.loaded)
{
calendar_sources_load_sources (sources,
&sources->priv->task_sources,
CALENDAR_SOURCES_TASK_SOURCES_KEY,
CALENDAR_SOURCES_SELECTED_TASK_SOURCES_KEY);
calendar_sources_load_esource_list (sources->priv->registry,
&sources->priv->task_sources);
sources->priv->task_sources.loaded = TRUE;
}
return sources->priv->task_sources.clients;
list = g_hash_table_get_values (sources->priv->task_sources.clients);
for (link = list; link != NULL; link = g_list_next (link))
link->data = ((ClientData *) link->data)->client;
return list;
}

View File

@ -58,8 +58,8 @@ struct _CalendarSourcesClass
GType calendar_sources_get_type (void) G_GNUC_CONST;
CalendarSources *calendar_sources_get (void);
GSList *calendar_sources_get_appointment_sources (CalendarSources *sources);
GSList *calendar_sources_get_task_sources (CalendarSources *sources);
GList *calendar_sources_get_appointment_clients (CalendarSources *sources);
GList *calendar_sources_get_task_clients (CalendarSources *sources);
G_END_DECLS

View File

@ -37,13 +37,7 @@
#include <gtk/gtk.h>
#define HANDLE_LIBICAL_MEMORY
#include <libecal/e-cal-client.h>
#include <libecal/e-cal-time-util.h>
#include <libecal/e-cal-recur.h>
#include <libecal/e-cal-system-timezone.h>
#define CALENDAR_CONFIG_PREFIX "/apps/evolution/calendar"
#define CALENDAR_CONFIG_TIMEZONE CALENDAR_CONFIG_PREFIX "/display/timezone"
#include <libecal/libecal.h>
#include "calendar-sources.h"
@ -90,7 +84,7 @@ typedef struct
{
char *uid;
char *rid;
char *uri;
char *backend_name;
char *summary;
char *description;
char *color_string;
@ -253,37 +247,60 @@ static char *
get_source_color (ECalClient *esource)
{
ESource *source;
ECalClientSourceType source_type;
ESourceSelectable *extension;
const gchar *extension_name;
g_return_val_if_fail (E_IS_CAL_CLIENT (esource), NULL);
source = e_client_get_source (E_CLIENT (esource));
source_type = e_cal_client_get_source_type (esource);
return g_strdup (e_source_peek_color_spec (source));
switch (source_type)
{
case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
extension_name = E_SOURCE_EXTENSION_CALENDAR;
break;
case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
extension_name = E_SOURCE_EXTENSION_TASK_LIST;
break;
default:
g_return_val_if_reached (NULL);
}
extension = e_source_get_extension (source, extension_name);
return e_source_selectable_dup_color (extension);
}
static gchar *
get_source_uri (ECalClient *esource)
get_source_backend_name (ECalClient *esource)
{
ESource *source;
gchar *string;
gchar **list;
ESource *source;
ECalClientSourceType source_type;
ESourceBackend *extension;
const gchar *extension_name;
g_return_val_if_fail (E_IS_CAL_CLIENT (esource), NULL);
g_return_val_if_fail (E_IS_CAL_CLIENT (esource), NULL);
source = e_client_get_source (E_CLIENT (esource));
string = g_strdup (e_source_get_uri (source));
if (string) {
list = g_strsplit (string, ":", 2);
g_free (string);
source = e_client_get_source (E_CLIENT (esource));
source_type = e_cal_client_get_source_type (esource);
if (list[0]) {
string = g_strdup (list[0]);
g_strfreev (list);
return string;
}
g_strfreev (list);
switch (source_type)
{
case E_CAL_CLIENT_SOURCE_TYPE_EVENTS:
extension_name = E_SOURCE_EXTENSION_CALENDAR;
break;
case E_CAL_CLIENT_SOURCE_TYPE_TASKS:
extension_name = E_SOURCE_EXTENSION_TASK_LIST;
break;
default:
g_return_val_if_reached (NULL);
}
return NULL;
extension = e_source_get_extension (source, extension_name);
return e_source_backend_dup_backend_name (extension);
}
static inline int
@ -314,7 +331,7 @@ calendar_appointment_equal (CalendarAppointment *a,
return
null_safe_strcmp (a->uid, b->uid) == 0 &&
null_safe_strcmp (a->uri, b->uri) == 0 &&
null_safe_strcmp (a->backend_name, b->backend_name) == 0 &&
null_safe_strcmp (a->summary, b->summary) == 0 &&
null_safe_strcmp (a->description, b->description) == 0 &&
null_safe_strcmp (a->color_string, b->color_string) == 0 &&
@ -339,8 +356,8 @@ calendar_appointment_free (CalendarAppointment *appointment)
g_free (appointment->rid);
appointment->rid = NULL;
g_free (appointment->uri);
appointment->uri = NULL;
g_free (appointment->backend_name);
appointment->backend_name = NULL;
g_free (appointment->summary);
appointment->summary = NULL;
@ -363,7 +380,7 @@ calendar_appointment_init (CalendarAppointment *appointment,
{
appointment->uid = get_ical_uid (ical);
appointment->rid = get_ical_rid (ical);
appointment->uri = get_source_uri (cal);
appointment->backend_name = get_source_backend_name (cal);
appointment->summary = get_ical_summary (ical);
appointment->description = get_ical_description (ical);
appointment->color_string = get_source_color (cal);
@ -467,9 +484,6 @@ struct _App
CalendarSources *sources;
gulong sources_signal_id;
guint zone_listener;
GConfClient *gconf_client;
/* hash from uid to CalendarAppointment objects */
GHashTable *appointments;
@ -585,8 +599,8 @@ on_objects_removed (ECalClientView *view,
static void
app_load_events (App *app)
{
GSList *sources;
GSList *l;
GList *clients;
GList *l;
GList *ll;
gchar *since_iso8601;
gchar *until_iso8601;
@ -616,8 +630,8 @@ app_load_events (App *app)
since_iso8601,
until_iso8601);
sources = calendar_sources_get_appointment_sources (app->sources);
for (l = sources; l != NULL; l = l->next)
clients = calendar_sources_get_appointment_clients (app->sources);
for (l = clients; l != NULL; l = l->next)
{
ECalClient *cal = E_CAL_CLIENT (l->data);
GError *error;
@ -630,8 +644,9 @@ app_load_events (App *app)
error = NULL;
if (!e_client_open_sync (E_CLIENT (cal), TRUE, NULL, &error))
{
ESource *source = e_client_get_source (E_CLIENT (cal));
g_warning ("Error opening calendar %s: %s\n",
e_client_get_uri (E_CLIENT (cal)), error->message);
e_source_get_uid (source), error->message);
g_error_free (error);
continue;
}
@ -648,8 +663,9 @@ app_load_events (App *app)
NULL, /* cancellable */
&error))
{
ESource *source = e_client_get_source (E_CLIENT (cal));
g_warning ("Error querying calendar %s: %s\n",
e_client_get_uri (E_CLIENT (cal)), error->message);
e_source_get_uid (source), error->message);
g_error_free (error);
g_free (query);
continue;
@ -705,6 +721,7 @@ app_load_events (App *app)
g_free (query);
}
g_list_free (clients);
g_free (since_iso8601);
g_free (until_iso8601);
app->cache_invalid = FALSE;

View File

@ -18,10 +18,10 @@
#include <meta/main.h>
#include <meta/meta-plugin.h>
#include <meta/prefs.h>
#include <atk-bridge.h>
#include <telepathy-glib/debug.h>
#include <telepathy-glib/debug-sender.h>
#include "shell-a11y.h"
#include "shell-global.h"
#include "shell-global-private.h"
#include "shell-js.h"
@ -235,6 +235,20 @@ shell_perf_log_init (void)
NULL, NULL);
}
static void
shell_a11y_init (void)
{
if (clutter_get_accessibility_enabled () == FALSE)
{
g_warning ("Accessibility: clutter has no accessibility enabled"
" skipping the atk-bridge load");
}
else
{
atk_bridge_adaptor_init (NULL, NULL);
}
}
static void
default_log_handler (const char *log_domain,
GLogLevelFlags log_level,
@ -358,13 +372,11 @@ main (int argc, char **argv)
g_option_context_free (ctx);
meta_plugin_type_register (gnome_shell_plugin_get_type ());
meta_plugin_manager_set_plugin_type (gnome_shell_plugin_get_type ());
/* Prevent meta_init() from causing gtk to load gail and at-bridge */
g_setenv ("NO_GAIL", "1", TRUE);
g_setenv ("NO_AT_BRIDGE", "1", TRUE);
meta_init ();
g_unsetenv ("NO_GAIL");
g_unsetenv ("NO_AT_BRIDGE");
/* FIXME: Add gjs API to set this stuff and don't depend on the

View File

@ -144,6 +144,9 @@ main(int argc, char **argv)
exit (1);
}
gjs_context_gc (js_context);
gjs_context_gc (js_context);
g_free (script);
exit (code);
}

View File

@ -1,164 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2010 Igalia, S.L.
*
* Author: Alejandro Piñeiro Iglesias <apinheiro@igalia.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#include <string.h>
#include <gmodule.h>
#include <clutter/clutter.h>
#include "shell-a11y.h"
#define INIT_METHOD "gnome_accessibility_module_init"
#define DESKTOP_SCHEMA "org.gnome.desktop.interface"
#define ACCESSIBILITY_ENABLED_KEY "toolkit-accessibility"
#define AT_SPI_SCHEMA "org.a11y.atspi"
#define ATK_BRIDGE_LOCATION_KEY "atk-bridge-location"
static gboolean
should_enable_a11y (void)
{
GSettings *desktop_settings = NULL;
gboolean value = FALSE;
desktop_settings = g_settings_new (DESKTOP_SCHEMA);
value = g_settings_get_boolean (desktop_settings, ACCESSIBILITY_ENABLED_KEY);
g_object_unref (desktop_settings);
return value;
}
static char*
get_atk_bridge_path (void)
{
GSettings *atspi_settings = NULL;
GVariant *variant = NULL;
char *value = NULL;
const char * const *schemas = NULL;
gboolean found = FALSE;
int i = 0;
schemas = g_settings_list_schemas ();
for (i = 0; schemas [i]; i++)
{
if (!strcmp (schemas[i], AT_SPI_SCHEMA))
{
found = TRUE;
break;
}
}
if (!found)
{
g_warning ("Accessibility: %s schema not found. Are you sure that at-spi or"
" at-spi2 is installed on your system?", AT_SPI_SCHEMA);
return NULL;
}
atspi_settings = g_settings_new (AT_SPI_SCHEMA);
variant = g_settings_get_value (atspi_settings, ATK_BRIDGE_LOCATION_KEY);
value = g_variant_dup_bytestring (variant, NULL);
g_variant_unref (variant);
g_object_unref (atspi_settings);
return value;
}
static gboolean
a11y_invoke_module (const char *module_path)
{
GModule *handle;
void (*invoke_fn) (void);
if (!module_path)
{
g_warning ("Accessibility: invalid module path (NULL)");
return FALSE;
}
if (!(handle = g_module_open (module_path, 0)))
{
g_warning ("Accessibility: failed to load module '%s': '%s'",
module_path, g_module_error ());
return FALSE;
}
if (!g_module_symbol (handle, INIT_METHOD, (gpointer *)&invoke_fn))
{
g_warning ("Accessibility: error library '%s' does not include "
"method '%s' required for accessibility support",
module_path, INIT_METHOD);
g_module_close (handle);
return FALSE;
}
invoke_fn ();
return TRUE;
}
/*
* It loads the atk-bridge if required. It checks:
* * If the proper gsetting key is set
* * If clutter has already enabled the accessibility
*
* You need to ensure that the atk-bridge was not loaded before this
* call, because in that case the application would be already
* registered on at-spi using the AtkUtil implementation on that
* moment (if any, although without anyone the application would
* crash). Anyway this is the reason of NO_AT_BRIDGE.
*
*/
void
shell_a11y_init (void)
{
char *bridge_path = NULL;
if (!should_enable_a11y ())
return;
if (clutter_get_accessibility_enabled () == FALSE)
{
g_warning ("Accessibility: clutter has no accessibility enabled"
" skipping the atk-bridge load");
return;
}
bridge_path = get_atk_bridge_path ();
if (a11y_invoke_module (bridge_path) == FALSE)
{
g_warning ("Accessibility: error loading the atk-bridge. Although the"
" accessibility on the system is enabled and clutter"
" accessibility is also enabled, accessibility support on"
" GNOME Shell will not work");
}
/* NOTE: We avoid to load gail module, as gail-cally interaction is
* not fully supported right now.
*/
g_free (bridge_path);
}

View File

@ -1,32 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2010 Igalia, S.L.
*
* Author: Alejandro Piñeiro Iglesias <apinheiro@igalia.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
#ifndef __SHELL_A11Y_H_
#define __SHELL_A11Y_H_
G_BEGIN_DECLS
void shell_a11y_init (void);
G_END_DECLS
#endif /* __SHELL_A11Y_H_ */

View File

@ -7,18 +7,13 @@
#include <string.h>
#include <gio/gio.h>
#include <gio/gdesktopappinfo.h>
#include <gtk/gtk.h>
#include <clutter/clutter.h>
#include <glib/gi18n.h>
#include <meta/display.h>
#include "shell-app-private.h"
#include "shell-window-tracker-private.h"
#include "shell-app-system-private.h"
#include "shell-global.h"
#include "shell-util.h"
#include "st.h"
/* Vendor prefixes are something that can be preprended to a .desktop
* file name. Undo this.
@ -46,6 +41,7 @@ struct _ShellAppSystemPrivate {
GMenuTree *apps_tree;
GHashTable *running_apps;
GHashTable *visible_id_to_app;
GHashTable *id_to_app;
GSList *known_vendor_prefixes;
@ -97,14 +93,16 @@ shell_app_system_init (ShellAppSystem *self)
priv->id_to_app = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL,
(GDestroyNotify)g_object_unref);
/* All the objects in this hash table are owned by id_to_app */
priv->visible_id_to_app = g_hash_table_new (g_str_hash, g_str_equal);
priv->setting_id_to_app = g_hash_table_new_full (g_str_hash, g_str_equal,
NULL,
(GDestroyNotify)g_object_unref);
/* For now, we want to pick up Evince, Nautilus, etc. We'll
* handle NODISPLAY semantics at a higher level or investigate them
* case by case.
*/
/* We want to track NoDisplay apps, so we add INCLUDE_NODISPLAY. We'll
* filter NoDisplay apps out when showing them to the user. */
priv->apps_tree = gmenu_tree_new ("applications.menu", GMENU_TREE_FLAGS_INCLUDE_NODISPLAY);
g_signal_connect (priv->apps_tree, "changed", G_CALLBACK (on_apps_tree_changed_cb), self);
@ -126,10 +124,10 @@ shell_app_system_finalize (GObject *object)
g_hash_table_destroy (priv->running_apps);
g_hash_table_destroy (priv->id_to_app);
g_hash_table_destroy (priv->visible_id_to_app);
g_hash_table_destroy (priv->setting_id_to_app);
g_slist_foreach (priv->known_vendor_prefixes, (GFunc)g_free, NULL);
g_slist_free (priv->known_vendor_prefixes);
g_slist_free_full (priv->known_vendor_prefixes, g_free);
priv->known_vendor_prefixes = NULL;
G_OBJECT_CLASS (shell_app_system_parent_class)->finalize (object);
@ -314,8 +312,8 @@ on_apps_tree_changed_cb (GMenuTree *tree,
g_assert (tree == self->priv->apps_tree);
g_slist_foreach (self->priv->known_vendor_prefixes, (GFunc)g_free, NULL);
g_slist_free (self->priv->known_vendor_prefixes);
g_hash_table_remove_all (self->priv->visible_id_to_app);
g_slist_free_full (self->priv->known_vendor_prefixes, g_free);
self->priv->known_vendor_prefixes = NULL;
if (!gmenu_tree_load_sync (self->priv->apps_tree, &error))
@ -376,6 +374,8 @@ on_apps_tree_changed_cb (GMenuTree *tree,
* string is pointed to.
*/
g_hash_table_replace (self->priv->id_to_app, (char*)id, app);
if (!gmenu_tree_entry_get_is_nodisplay_recurse (entry))
g_hash_table_replace (self->priv->visible_id_to_app, (char*)id, app);
if (old_entry)
gmenu_tree_item_unref (old_entry);
@ -644,30 +644,6 @@ shell_app_system_lookup_wmclass (ShellAppSystem *system,
return app;
}
/**
* shell_app_system_get_all:
* @system:
*
* Returns: (transfer container) (element-type ShellApp): All installed applications
*/
GSList *
shell_app_system_get_all (ShellAppSystem *self)
{
GSList *result = NULL;
GHashTableIter iter;
gpointer key, value;
g_hash_table_iter_init (&iter, self->priv->id_to_app);
while (g_hash_table_iter_next (&iter, &key, &value))
{
ShellApp *app = value;
if (!g_desktop_app_info_get_nodisplay (shell_app_get_app_info (app)))
result = g_slist_prepend (result, app);
}
return result;
}
void
_shell_app_system_notify_app_state_changed (ShellAppSystem *self,
ShellApp *app)
@ -790,8 +766,7 @@ search_tree (ShellAppSystem *self,
&prefix_results,
&substring_results);
}
g_slist_foreach (normalized_terms, (GFunc)g_free, NULL);
g_slist_free (normalized_terms);
g_slist_free_full (normalized_terms, g_free);
return sort_and_concat_results (self, prefix_results, substring_results);
@ -810,7 +785,7 @@ GSList *
shell_app_system_initial_search (ShellAppSystem *self,
GSList *terms)
{
return search_tree (self, terms, self->priv->id_to_app);
return search_tree (self, terms, self->priv->visible_id_to_app);
}
/**
@ -844,8 +819,7 @@ shell_app_system_subsearch (ShellAppSystem *system,
&prefix_results,
&substring_results);
}
g_slist_foreach (normalized_terms, (GFunc)g_free, NULL);
g_slist_free (normalized_terms);
g_slist_free_full (normalized_terms, g_free);
/* Note that a shorter term might have matched as a prefix, but
when extended only as a substring, so we have to redo the

View File

@ -52,8 +52,6 @@ ShellApp *shell_app_system_lookup_heuristic_basename (ShellAppSystem *
ShellApp *shell_app_system_lookup_wmclass (ShellAppSystem *system,
const char *wmclass);
GSList *shell_app_system_get_all (ShellAppSystem *system);
GSList *shell_app_system_get_running (ShellAppSystem *self);
GSList *shell_app_system_initial_search (ShellAppSystem *system,

View File

@ -42,6 +42,7 @@ typedef struct {
/* See GApplication documentation */
GDBusMenuModel *remote_menu;
GActionMuxer *muxer;
char * unique_bus_name;
} ShellAppRunningState;
/**
@ -95,7 +96,6 @@ enum {
static guint shell_app_signals[LAST_SIGNAL] = { 0 };
static void create_running_state (ShellApp *app);
static void setup_running_state (ShellApp *app, MetaWindow *window);
static void unref_running_state (ShellAppRunningState *state);
G_DEFINE_TYPE (ShellApp, shell_app, G_TYPE_OBJECT)
@ -975,7 +975,7 @@ _shell_app_add_window (ShellApp *app,
g_signal_connect (window, "unmanaged", G_CALLBACK(shell_app_on_unmanaged), app);
g_signal_connect (window, "notify::user-time", G_CALLBACK(shell_app_on_user_time_changed), app);
setup_running_state (app, window);
shell_app_update_app_menu (app, window);
if (app->state != SHELL_APP_STATE_STARTING)
shell_app_state_transition (app, SHELL_APP_STATE_RUNNING);
@ -1221,12 +1221,14 @@ create_running_state (ShellApp *app)
app->running_state->muxer = g_action_muxer_new ();
}
static void
setup_running_state (ShellApp *app,
MetaWindow *window)
void
shell_app_update_app_menu (ShellApp *app,
MetaWindow *window)
{
/* We assume that 'gtk-unique-bus-name', gtk-application-object-path'
* and 'gtk-app-menu-object-path' are the same for all windows which
const gchar *unique_bus_name;
/* We assume that 'gtk-application-object-path' and
* 'gtk-app-menu-object-path' are the same for all windows which
* have it set.
*
* It could be possible, however, that the first window we see
@ -1235,23 +1237,27 @@ setup_running_state (ShellApp *app,
* all the rest (until the app is stopped and restarted).
*/
if (app->running_state->remote_menu == NULL)
unique_bus_name = meta_window_get_gtk_unique_bus_name (window);
if (app->running_state->remote_menu == NULL ||
g_strcmp0 (app->running_state->unique_bus_name, unique_bus_name) != 0)
{
const gchar *application_object_path;
const gchar *app_menu_object_path;
const gchar *unique_bus_name;
GDBusConnection *session;
GDBusActionGroup *actions;
application_object_path = meta_window_get_gtk_application_object_path (window);
app_menu_object_path = meta_window_get_gtk_app_menu_object_path (window);
unique_bus_name = meta_window_get_gtk_unique_bus_name (window);
if (application_object_path == NULL || app_menu_object_path == NULL || unique_bus_name == NULL)
return;
session = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
g_assert (session != NULL);
g_clear_pointer (&app->running_state->unique_bus_name, g_free);
app->running_state->unique_bus_name = g_strdup (unique_bus_name);
g_clear_object (&app->running_state->remote_menu);
app->running_state->remote_menu = g_dbus_menu_model_get (session, unique_bus_name, app_menu_object_path);
actions = g_dbus_action_group_get (session, unique_bus_name, application_object_path);
g_action_muxer_insert (app->running_state->muxer, "app", G_ACTION_GROUP (actions));

View File

@ -82,6 +82,7 @@ int shell_app_compare_by_name (ShellApp *app, ShellApp *other);
int shell_app_compare (ShellApp *app, ShellApp *other);
void shell_app_update_window_actions (ShellApp *app, MetaWindow *window);
void shell_app_update_app_menu (ShellApp *app, MetaWindow *window);
G_END_DECLS

View File

@ -297,7 +297,7 @@ sort_and_prepare_results (GSList *results)
sorted_results = g_slist_prepend (sorted_results, id);
}
g_slist_foreach (results, (GFunc) free_result, NULL);
g_slist_free_full (results, (GDestroyNotify) free_result);
return sorted_results;
}
@ -463,6 +463,9 @@ shell_contact_system_initial_search (ShellContactSystem *self,
g_object_unref (individual);
}
g_object_unref (iter);
g_slist_free_full (normalized_terms, (GDestroyNotify) g_free);
return sort_and_prepare_results (results);
}

View File

@ -230,6 +230,7 @@ auth_request_free (AuthRequest *request)
g_free (request->message);
g_free (request->icon_name);
g_object_unref (request->details);
g_free (request->cookie);
g_list_foreach (request->identities, (GFunc) g_object_unref, NULL);
g_list_free (request->identities);
g_object_unref (request->simple);

View File

@ -341,3 +341,9 @@ shell_decline_dispatch_op (TpAddDispatchOperationContext *context,
tp_add_dispatch_operation_context_fail (context, error);
g_error_free (error);
}
/* gjs doesn't cope with tp_proxy_get_invalidated() returning a GError */
gboolean shell_is_channel_invalidated (TpChannel *channel)
{
return tp_proxy_get_invalidated (channel) != NULL;
}

View File

@ -105,5 +105,7 @@ void shell_tp_client_grab_contact_list_changed (ShellTpClient *self,
void shell_decline_dispatch_op (TpAddDispatchOperationContext *context,
const gchar *message);
gboolean shell_is_channel_invalidated (TpChannel *channel);
G_END_DECLS
#endif /* __SHELL_TP_CLIENT_H__ */

View File

@ -96,6 +96,7 @@ shell_util_get_file_display_for_common_files (GFile *file)
* nautilus */
return g_strdup (_("Home"));
}
g_object_unref (compare);
compare = g_file_new_for_path ("/");
if (g_file_equal (file, compare))

View File

@ -357,7 +357,10 @@ update_focus_app (ShellWindowTracker *self)
new_focus_app = new_focus_win ? shell_window_tracker_get_window_app (self, new_focus_win) : NULL;
if (new_focus_app)
shell_app_update_window_actions (new_focus_app, new_focus_win);
{
shell_app_update_window_actions (new_focus_app, new_focus_win);
shell_app_update_app_menu (new_focus_app, new_focus_win);
}
set_focus_app (self, new_focus_app);
}

View File

@ -933,6 +933,7 @@ load_gicon_with_colors (StTextureCache *cache,
if (ensure_request (cache, key, policy, &request, texture))
{
/* If there's an outstanding request, we've just added ourselves to it */
gtk_icon_info_free (info);
g_free (key);
}
else

View File

@ -52,6 +52,7 @@ G_DEFINE_TYPE (StThemeContext, st_theme_context, G_TYPE_OBJECT)
static void on_icon_theme_changed (StTextureCache *cache,
StThemeContext *context);
static void st_theme_context_changed (StThemeContext *context);
static void
st_theme_context_finalize (GObject *object)
@ -61,6 +62,9 @@ st_theme_context_finalize (GObject *object)
g_signal_handlers_disconnect_by_func (st_texture_cache_get_default (),
(gpointer) on_icon_theme_changed,
context);
g_signal_handlers_disconnect_by_func (clutter_get_default_backend (),
(gpointer) st_theme_context_changed,
context);
if (context->root_node)
g_object_unref (context->root_node);
@ -97,6 +101,10 @@ st_theme_context_init (StThemeContext *context)
"icon-theme-changed",
G_CALLBACK (on_icon_theme_changed),
context);
g_signal_connect_swapped (clutter_get_default_backend (),
"resolution-changed",
G_CALLBACK (st_theme_context_changed),
context);
}
/**

View File

@ -67,18 +67,6 @@ st_theme_node_dispose (GObject *gobject)
{
StThemeNode *node = ST_THEME_NODE (gobject);
if (node->context)
{
g_object_unref (node->context);
node->context = NULL;
}
if (node->theme)
{
g_object_unref (node->theme);
node->theme = NULL;
}
if (node->parent_node)
{
g_object_unref (node->parent_node);
@ -97,6 +85,8 @@ st_theme_node_dispose (GObject *gobject)
node->icon_colors = NULL;
}
g_clear_object (&node->theme);
G_OBJECT_CLASS (st_theme_node_parent_class)->dispose (gobject);
}
@ -194,7 +184,7 @@ st_theme_node_new (StThemeContext *context,
node = g_object_new (ST_TYPE_THEME_NODE, NULL);
node->context = g_object_ref (context);
node->context = context;
if (parent_node != NULL)
node->parent_node = g_object_ref (parent_node);
else
@ -204,7 +194,10 @@ st_theme_node_new (StThemeContext *context,
theme = parent_node->theme;
if (theme != NULL)
node->theme = g_object_ref (theme);
node->theme = theme;
if (node->theme != NULL)
g_object_ref (node->theme);
node->element_type = element_type;
node->element_id = g_strdup (element_id);

View File

@ -317,6 +317,7 @@ st_widget_finalize (GObject *gobject)
g_free (priv->pseudo_class);
g_object_unref (priv->local_state_set);
g_free (priv->accessible_name);
g_free (priv->inline_style);
G_OBJECT_CLASS (st_widget_parent_class)->finalize (gobject);
}
@ -762,18 +763,17 @@ st_widget_get_paint_volume (ClutterActor *self,
static GList *
st_widget_real_get_focus_chain (StWidget *widget)
{
GList *children;
ClutterActorIter iter;
ClutterActor *child;
GList *focus_chain = NULL;
for (children = clutter_actor_get_children (CLUTTER_ACTOR (widget));
children;
children = children->next)
clutter_actor_iter_init (&iter, CLUTTER_ACTOR (widget));
while (clutter_actor_iter_next (&iter, &child))
{
ClutterActor *child = children->data;
if (CLUTTER_ACTOR_IS_VISIBLE (child))
focus_chain = g_list_prepend (focus_chain, child);
}
return g_list_reverse (focus_chain);
}
@ -1011,7 +1011,7 @@ st_widget_class_init (StWidgetClass *klass)
*/
void
st_widget_set_theme (StWidget *actor,
StTheme *theme)
StTheme *theme)
{
StWidgetPrivate *priv;
@ -1019,11 +1019,11 @@ st_widget_set_theme (StWidget *actor,
priv = actor->priv;
if (theme !=priv->theme)
if (theme != priv->theme)
{
if (priv->theme)
g_object_unref (priv->theme);
priv->theme = g_object_ref (priv->theme);
priv->theme = g_object_ref (theme);
st_widget_style_changed (actor);
@ -2503,6 +2503,8 @@ st_widget_accessible_dispose (GObject *gobject)
g_object_unref (self->priv->current_label);
self->priv->current_label = NULL;
}
G_OBJECT_CLASS (st_widget_accessible_parent_class)->dispose (gobject);
}
static void

View File

@ -425,6 +425,9 @@ main (int argc, char **argv)
{
StTheme *theme;
StThemeContext *context;
PangoFontDescription *font_desc;
gtk_init (&argc, &argv);
if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
return 1;
@ -435,8 +438,10 @@ main (int argc, char **argv)
stage = clutter_stage_new ();
context = st_theme_context_get_for_stage (CLUTTER_STAGE (stage));
st_theme_context_set_theme (context, theme);
st_theme_context_set_font (context,
pango_font_description_from_string ("sans-serif 12"));
font_desc = pango_font_description_from_string ("sans-serif 12");
st_theme_context_set_font (context, font_desc);
pango_font_description_free (font_desc);
root = st_theme_context_get_root_node (context);
group1 = st_theme_node_new (context, root, NULL,
@ -469,5 +474,17 @@ main (int argc, char **argv)
test_pseudo_class ();
test_inline_style ();
g_object_unref (cairo_texture);
g_object_unref (group1);
g_object_unref (group2);
g_object_unref (group3);
g_object_unref (text1);
g_object_unref (text2);
g_object_unref (text3);
g_object_unref (text4);
g_object_unref (theme);
clutter_actor_destroy (stage);
return fail ? 1 : 0;
}

View File

@ -6,99 +6,97 @@ const St = imports.gi.St;
const UI = imports.testcommon.ui;
UI.init();
let stage = Clutter.Stage.get_default();
stage.user_resizable = true;
stage.width = 1024;
stage.height = 768;
function test() {
let stage = new Clutter.Stage({ user_resizable: true, width: 1024, height: 768 });
UI.init(stage);
let vbox = new St.BoxLayout({ style: 'background: #ffee88;' });
vbox.add_constraint(new Clutter.BindConstraint({ source: stage,
coordinate: Clutter.BindCoordinate.SIZE }));
stage.add_actor(vbox);
let vbox = new St.BoxLayout({ style: 'background: #ffee88;' });
vbox.add_constraint(new Clutter.BindConstraint({ source: stage,
coordinate: Clutter.BindCoordinate.SIZE }));
stage.add_actor(vbox);
let scroll = new St.ScrollView();
vbox.add(scroll, { expand: true });
let scroll = new St.ScrollView();
vbox.add(scroll, { expand: true });
let vbox = new St.BoxLayout({ vertical: true,
style: 'padding: 10px;'
+ 'spacing: 20px;' });
scroll.add_actor(vbox);
let vbox = new St.BoxLayout({ vertical: true,
style: 'padding: 10px;'
+ 'spacing: 20px;' });
scroll.add_actor(vbox);
let tbox = null;
let tbox = null;
function addTestCase(image, size, backgroundSize, useCairo) {
// Using a border in CSS forces cairo rendering.
// To get a border using cogl, we paint a border using
// paint signal hacks.
function addTestCase(image, size, backgroundSize, useCairo) {
// Using a border in CSS forces cairo rendering.
// To get a border using cogl, we paint a border using
// paint signal hacks.
let obin = new St.Bin();
if (useCairo)
obin.style = 'border: 3px solid green;';
else
obin.connect_after('paint', function(actor) {
Cogl.set_source_color4f(0, 1, 0, 1);
let obin = new St.Bin();
if (useCairo)
obin.style = 'border: 3px solid green;';
else
obin.connect_after('paint', function(actor) {
Cogl.set_source_color4f(0, 1, 0, 1);
let geom = actor.get_allocation_geometry();
let width = 3;
let geom = actor.get_allocation_geometry();
let width = 3;
// clockwise order
Cogl.rectangle(0, 0, geom.width, width);
Cogl.rectangle(geom.width - width, width,
geom.width, geom.height);
Cogl.rectangle(0, geom.height,
geom.width - width, geom.height - width);
Cogl.rectangle(0, geom.height - width,
width, width);
});
tbox.add(obin);
// clockwise order
Cogl.rectangle(0, 0, geom.width, width);
Cogl.rectangle(geom.width - width, width,
geom.width, geom.height);
Cogl.rectangle(0, geom.height,
geom.width - width, geom.height - width);
Cogl.rectangle(0, geom.height - width,
width, width);
});
tbox.add(obin);
let [width, height] = size;
let bin = new St.Bin({ style_class: 'background-image-' + image,
width: width,
height: height,
style: 'border: 1px solid transparent;'
+ 'background-size: ' + backgroundSize + ';',
x_fill: true,
y_fill: true
});
obin.set_child(bin);
let [width, height] = size;
let bin = new St.Bin({ style_class: 'background-image-' + image,
width: width,
height: height,
style: 'border: 1px solid transparent;'
+ 'background-size: ' + backgroundSize + ';',
x_fill: true,
y_fill: true
});
obin.set_child(bin);
bin.set_child(new St.Label({ text: backgroundSize + (useCairo ? ' (cairo)' : ' (cogl)'),
style: 'font-size: 15px;'
+ 'text-align: center;'
}));
bin.set_child(new St.Label({ text: backgroundSize + (useCairo ? ' (cairo)' : ' (cogl)'),
style: 'font-size: 15px;'
+ 'text-align: center;'
}));
}
function addTestLine(image, size, useCairo) {
const backgroundSizes = ["auto", "contain", "cover", "200px 200px", "100px 100px", "100px 200px"];
let [width, height] = size;
vbox.add(new St.Label({ text: image + '.svg / ' + width + '×' + height,
style: 'font-size: 15px;'
+ 'text-align: center;'
}));
tbox = new St.BoxLayout({ style: 'spacing: 20px;' });
vbox.add(tbox);
for each (let s in backgroundSizes)
addTestCase(image, size, s, false);
for each (let s in backgroundSizes)
addTestCase(image, size, s, true);
}
function addTestImage(image) {
const containerSizes = [[100, 100], [200, 200], [250, 250], [100, 250], [250, 100]];
for each (let size in containerSizes)
addTestLine(image, size);
}
addTestImage ('200-200');
addTestImage ('200-100');
addTestImage ('100-200');
UI.main(stage);
}
function addTestLine(image, size, useCairo) {
const backgroundSizes = ["auto", "contain", "cover", "200px 200px", "100px 100px", "100px 200px"];
let [width, height] = size;
vbox.add(new St.Label({ text: image + '.svg / ' + width + '×' + height,
style: 'font-size: 15px;'
+ 'text-align: center;'
}));
tbox = new St.BoxLayout({ style: 'spacing: 20px;' });
vbox.add(tbox);
for each (let s in backgroundSizes)
addTestCase(image, size, s, false);
for each (let s in backgroundSizes)
addTestCase(image, size, s, true);
}
function addTestImage(image) {
const containerSizes = [[100, 100], [200, 200], [250, 250], [100, 250], [250, 100]];
for each (let size in containerSizes)
addTestLine(image, size);
}
addTestImage ('200-200');
addTestImage ('200-100');
addTestImage ('100-200');
stage.show();
Clutter.main();
stage.destroy();
test();

View File

@ -5,59 +5,58 @@ const St = imports.gi.St;
const UI = imports.testcommon.ui;
UI.init();
let stage = Clutter.Stage.get_default();
stage.width = 640;
stage.height = 480;
function test() {
let stage = new Clutter.Stage({ width: 640, height: 480 });
UI.init(stage);
let vbox = new St.BoxLayout({ width: stage.width,
height: stage.height,
style: 'background: #ffee88;' });
stage.add_actor(vbox);
let vbox = new St.BoxLayout({ width: stage.width,
height: stage.height,
style: 'background: #ffee88;' });
stage.add_actor(vbox);
let scroll = new St.ScrollView();
vbox.add(scroll, { expand: true });
let scroll = new St.ScrollView();
vbox.add(scroll, { expand: true });
let box = new St.BoxLayout({ vertical: true,
style: 'padding: 10px;'
+ 'spacing: 20px;' });
scroll.add_actor(box);
let box = new St.BoxLayout({ vertical: true,
style: 'padding: 10px;'
+ 'spacing: 20px;' });
scroll.add_actor(box);
function addTestCase(radii, useGradient) {
let background;
if (useGradient)
background = 'background-gradient-direction: vertical;'
+ 'background-gradient-start: white;'
+ 'background-gradient-end: gray;';
else
background = 'background: white;';
function addTestCase(radii, useGradient) {
let background;
if (useGradient)
background = 'background-gradient-direction: vertical;'
+ 'background-gradient-start: white;'
+ 'background-gradient-end: gray;';
else
background = 'background: white;';
box.add(new St.Label({ text: "border-radius: " + radii + ";",
style: 'border: 1px solid black; '
+ 'border-radius: ' + radii + ';'
+ 'padding: 5px;' + background }),
{ x_fill: false });
box.add(new St.Label({ text: "border-radius: " + radii + ";",
style: 'border: 1px solid black; '
+ 'border-radius: ' + radii + ';'
+ 'padding: 5px;' + background }),
{ x_fill: false });
}
// uniform backgrounds
addTestCase(" 0px 5px 10px 15px", false);
addTestCase(" 5px 10px 15px 0px", false);
addTestCase("10px 15px 0px 5px", false);
addTestCase("15px 0px 5px 10px", false);
// gradient backgrounds
addTestCase(" 0px 5px 10px 15px", true);
addTestCase(" 5px 10px 15px 0px", true);
addTestCase("10px 15px 0px 5px", true);
addTestCase("15px 0px 5px 10px", true);
// border-radius reduction
// these should all take the cairo fallback,
// so don't bother testing w/ or w/out gradients.
addTestCase("200px 200px 200px 200px", false);
addTestCase("200px 200px 0px 200px", false);
addTestCase("999px 0px 999px 0px", false);
UI.main(stage);
}
// uniform backgrounds
addTestCase(" 0px 5px 10px 15px", false);
addTestCase(" 5px 10px 15px 0px", false);
addTestCase("10px 15px 0px 5px", false);
addTestCase("15px 0px 5px 10px", false);
// gradient backgrounds
addTestCase(" 0px 5px 10px 15px", true);
addTestCase(" 5px 10px 15px 0px", true);
addTestCase("10px 15px 0px 5px", true);
addTestCase("15px 0px 5px 10px", true);
// border-radius reduction
// these should all take the cairo fallback,
// so don't bother testing w/ or w/out gradients.
addTestCase("200px 200px 200px 200px", false);
addTestCase("200px 200px 0px 200px", false);
addTestCase("999px 0px 999px 0px", false);
stage.show();
Clutter.main();
stage.destroy();
test();

View File

@ -5,56 +5,55 @@ const St = imports.gi.St;
const UI = imports.testcommon.ui;
UI.init();
let stage = Clutter.Stage.get_default();
stage.width = 640;
stage.height = 480;
function test() {
let stage = new Clutter.Stage({ width: 640, height: 480 });
UI.init(stage);
let vbox = new St.BoxLayout({ width: stage.width,
height: stage.height,
style: 'padding: 10px; background: #ffee88;'
});
stage.add_actor(vbox);
let vbox = new St.BoxLayout({ width: stage.width,
height: stage.height,
style: 'padding: 10px; background: #ffee88;'
});
stage.add_actor(vbox);
let scroll = new St.ScrollView();
vbox.add(scroll, { expand: true });
let scroll = new St.ScrollView();
vbox.add(scroll, { expand: true });
let box = new St.BoxLayout({ vertical: true,
style: 'spacing: 20px;' });
scroll.add_actor(box);
let box = new St.BoxLayout({ vertical: true,
style: 'spacing: 20px;' });
scroll.add_actor(box);
function addTestCase(borders, useGradient) {
let background;
if (useGradient)
background = 'background-gradient-direction: vertical;'
+ 'background-gradient-start: white;'
+ 'background-gradient-end: gray;';
else
background = 'background: white;';
function addTestCase(borders, useGradient) {
let background;
if (useGradient)
background = 'background-gradient-direction: vertical;'
+ 'background-gradient-start: white;'
+ 'background-gradient-end: gray;';
else
background = 'background: white;';
let border_style = "border-top: " + borders[St.Side.TOP] + " solid black;\n" +
"border-right: " + borders[St.Side.RIGHT] + " solid black;\n" +
"border-bottom: " + borders[St.Side.BOTTOM] + " solid black;\n" +
"border-left: " + borders[St.Side.LEFT] + " solid black;";
box.add(new St.Label({ text: border_style,
style: border_style
+ 'border-radius: 0px 5px 15px 25px;'
+ 'padding: 5px;' + background }),
{ x_fill: false });
let border_style = "border-top: " + borders[St.Side.TOP] + " solid black;\n" +
"border-right: " + borders[St.Side.RIGHT] + " solid black;\n" +
"border-bottom: " + borders[St.Side.BOTTOM] + " solid black;\n" +
"border-left: " + borders[St.Side.LEFT] + " solid black;";
box.add(new St.Label({ text: border_style,
style: border_style
+ 'border-radius: 0px 5px 15px 25px;'
+ 'padding: 5px;' + background }),
{ x_fill: false });
}
// uniform backgrounds
addTestCase([" 0px", " 5px", "10px", "15px"], false);
addTestCase([" 5px", "10px", "15px", " 0px"], false);
addTestCase(["10px", "15px", " 0px", " 5px"], false);
addTestCase(["15px", " 0px", " 5px", "10px"], false);
// gradient backgrounds
addTestCase([" 0px", " 5px", "10px", "15px"], true);
addTestCase([" 5px", "10px", "15px", " 0px"], true);
addTestCase(["10px", "15px", " 0px", " 5px"], true);
addTestCase(["15px", " 0px", " 5px", "10px"], true);
UI.main(stage);
}
// uniform backgrounds
addTestCase([" 0px", " 5px", "10px", "15px"], false);
addTestCase([" 5px", "10px", "15px", " 0px"], false);
addTestCase(["10px", "15px", " 0px", " 5px"], false);
addTestCase(["15px", " 0px", " 5px", "10px"], false);
// gradient backgrounds
addTestCase([" 0px", " 5px", "10px", "15px"], true);
addTestCase([" 5px", "10px", "15px", " 0px"], true);
addTestCase(["10px", "15px", " 0px", " 5px"], true);
addTestCase(["15px", " 0px", " 5px", "10px"], true);
stage.show();
Clutter.main();
stage.destroy();
test();

View File

@ -5,131 +5,131 @@ const St = imports.gi.St;
const UI = imports.testcommon.ui;
UI.init();
let stage = Clutter.Stage.get_default();
stage.width = 640;
stage.height = 480;
function test() {
let stage = new Clutter.Stage({ width: 640, height: 480 });
UI.init(stage);
let vbox = new St.BoxLayout({ width: stage.width,
height: stage.height,
style: 'background: #ffee88;' });
stage.add_actor(vbox);
let vbox = new St.BoxLayout({ width: stage.width,
height: stage.height,
style: 'background: #ffee88;' });
stage.add_actor(vbox);
let scroll = new St.ScrollView();
vbox.add(scroll, { expand: true });
let scroll = new St.ScrollView();
vbox.add(scroll, { expand: true });
let box = new St.BoxLayout({ vertical: true,
style: 'padding: 10px;'
+ 'spacing: 20px;' });
scroll.add_actor(box);
let box = new St.BoxLayout({ vertical: true,
style: 'padding: 10px;'
+ 'spacing: 20px;' });
scroll.add_actor(box);
box.add(new St.Label({ text: "Hello World",
style: 'border: 1px solid black; '
+ 'padding: 5px;' }));
box.add(new St.Label({ text: "Hello World",
style: 'border: 1px solid black; '
+ 'padding: 5px;' }));
box.add(new St.Label({ text: "Hello Round World",
style: 'border: 3px solid green; '
+ 'border-radius: 8px; '
+ 'padding: 5px;' }));
box.add(new St.Label({ text: "Hello Round World",
style: 'border: 3px solid green; '
+ 'border-radius: 8px; '
+ 'padding: 5px;' }));
box.add(new St.Label({ text: "Hello Background",
style: 'border: 3px solid green; '
+ 'border-radius: 8px; '
+ 'background: white; '
+ 'padding: 5px;' }));
box.add(new St.Label({ text: "Hello Background",
style: 'border: 3px solid green; '
+ 'border-radius: 8px; '
+ 'background: white; '
+ 'padding: 5px;' }));
box.add(new St.Label({ text: "Hello Translucent Black Border",
style: 'border: 3px solid rgba(0, 0, 0, 0.4); '
+ 'background: white; ' }));
box.add(new St.Label({ text: "Hello Translucent Black Border",
style: 'border: 3px solid rgba(0, 0, 0, 0.4); '
+ 'background: white; ' }));
box.add(new St.Label({ text: "Hello Translucent Background",
style: 'background: rgba(255, 255, 255, 0.3);' }));
box.add(new St.Label({ text: "Hello Translucent Background",
style: 'background: rgba(255, 255, 255, 0.3);' }));
box.add(new St.Label({ text: "Border, Padding, Content: 20px" }));
box.add(new St.Label({ text: "Border, Padding, Content: 20px" }));
let b1 = new St.BoxLayout({ vertical: true,
style: 'border: 20px solid black; '
+ 'background: white; '
+ 'padding: 20px;' });
box.add(b1);
let b1 = new St.BoxLayout({ vertical: true,
style: 'border: 20px solid black; '
+ 'background: white; '
+ 'padding: 20px;' });
box.add(b1);
b1.add(new St.BoxLayout({ width: 20, height: 20,
style: 'background: black' }));
b1.add(new St.BoxLayout({ width: 20, height: 20,
style: 'background: black' }));
box.add(new St.Label({ text: "Translucent big blue border, with rounding",
style: 'border: 20px solid rgba(0, 0, 255, 0.2); '
+ 'border-radius: 10px; '
+ 'background: white; '
+ 'padding: 10px;' }));
box.add(new St.Label({ text: "Translucent big blue border, with rounding",
style: 'border: 20px solid rgba(0, 0, 255, 0.2); '
+ 'border-radius: 10px; '
+ 'background: white; '
+ 'padding: 10px;' }));
box.add(new St.Label({ text: "Transparent border",
style: 'border: 20px solid transparent; '
+ 'background: white; '
+ 'padding: 10px;' }));
box.add(new St.Label({ text: "Transparent border",
style: 'border: 20px solid transparent; '
+ 'background: white; '
+ 'padding: 10px;' }));
box.add(new St.Label({ text: "Border Image",
style_class: "border-image",
style: "padding: 10px;" }));
box.add(new St.Label({ text: "Border Image",
style_class: "border-image",
style: "padding: 10px;" }));
box.add(new St.Label({ text: "Border Image with Gradient",
style_class: 'border-image-with-background-gradient',
style: "padding: 10px;"
+ 'background-gradient-direction: vertical;' }));
box.add(new St.Label({ text: "Border Image with Gradient",
style_class: 'border-image-with-background-gradient',
style: "padding: 10px;"
+ 'background-gradient-direction: vertical;' }));
box.add(new St.Label({ text: "Rounded, framed, shadowed gradients" }));
box.add(new St.Label({ text: "Rounded, framed, shadowed gradients" }));
let framedGradients = new St.BoxLayout({ vertical: false,
style: 'padding: 10px; spacing: 12px;' });
box.add(framedGradients);
let framedGradients = new St.BoxLayout({ vertical: false,
style: 'padding: 10px; spacing: 12px;' });
box.add(framedGradients);
function addGradientCase(direction, borderWidth, borderRadius, extra) {
let gradientBox = new St.BoxLayout({ style_class: 'background-gradient',
style: 'border: ' + borderWidth + 'px solid #8b0000;'
+ 'border-radius: ' + borderRadius + 'px;'
+ 'background-gradient-direction: ' + direction + ';'
+ 'width: 32px;'
+ 'height: 32px;'
+ extra });
framedGradients.add(gradientBox, { x_fill: false, y_fill: false } );
}
addGradientCase ('horizontal', 0, 5, 'box-shadow: 0px 0px 0px 0px rgba(0,0,0,0.5);');
addGradientCase ('horizontal', 2, 5, 'box-shadow: 0px 2px 0px 0px rgba(0,255,0,0.5);');
addGradientCase ('horizontal', 5, 2, 'box-shadow: 2px 0px 0px 0px rgba(0,0,255,0.5);');
addGradientCase ('horizontal', 5, 20, 'box-shadow: 0px 0px 4px 0px rgba(255,0,0,0.5);');
addGradientCase ('vertical', 0, 5, 'box-shadow: 0px 0px 0px 4px rgba(0,0,0,0.5);');
addGradientCase ('vertical', 2, 5, 'box-shadow: 0px 0px 4px 4px rgba(0,0,0,0.5);');
addGradientCase ('vertical', 5, 2, 'box-shadow: -2px -2px 6px 0px rgba(0,0,0,0.5);');
addGradientCase ('vertical', 5, 20, 'box-shadow: -2px -2px 0px 6px rgba(0,0,0,0.5);');
box.add(new St.Label({ text: "Rounded, framed, shadowed images" }));
let framedImages = new St.BoxLayout({ vertical: false,
style: 'padding: 10px; spacing: 6px;' });
box.add(framedImages);
function addBackgroundImageCase(borderWidth, borderRadius, width, height, extra) {
let imageBox = new St.BoxLayout({ style_class: 'background-image',
style: 'border: ' + borderWidth + 'px solid #8b8b8b;'
function addGradientCase(direction, borderWidth, borderRadius, extra) {
let gradientBox = new St.BoxLayout({ style_class: 'background-gradient',
style: 'border: ' + borderWidth + 'px solid #8b0000;'
>>>>>>> 2aff593... tests: Run each test in a function
+ 'border-radius: ' + borderRadius + 'px;'
+ 'width: ' + width + 'px;'
+ 'height: ' + height + 'px;'
+ 'background-gradient-direction: ' + direction + ';'
+ 'width: 32px;'
+ 'height: 32px;'
+ extra });
framedImages.add(imageBox, { x_fill: false, y_fill: false } );
framedGradients.add(gradientBox, { x_fill: false, y_fill: false } );
}
addGradientCase ('horizontal', 0, 5, 'box-shadow: 0px 0px 0px 0px rgba(0,0,0,0.5);');
addGradientCase ('horizontal', 2, 5, 'box-shadow: 0px 2px 0px 0px rgba(0,255,0,0.5);');
addGradientCase ('horizontal', 5, 2, 'box-shadow: 2px 0px 0px 0px rgba(0,0,255,0.5);');
addGradientCase ('horizontal', 5, 20, 'box-shadow: 0px 0px 4px 0px rgba(255,0,0,0.5);');
addGradientCase ('vertical', 0, 5, 'box-shadow: 0px 0px 0px 4px rgba(0,0,0,0.5);');
addGradientCase ('vertical', 2, 5, 'box-shadow: 0px 0px 4px 4px rgba(0,0,0,0.5);');
addGradientCase ('vertical', 5, 2, 'box-shadow: -2px -2px 6px 0px rgba(0,0,0,0.5);');
addGradientCase ('vertical', 5, 20, 'box-shadow: -2px -2px 0px 6px rgba(0,0,0,0.5);');
box.add(new St.Label({ text: "Rounded, framed, shadowed images" }));
let framedImages = new St.BoxLayout({ vertical: false,
style: 'padding: 10px; spacing: 6px;' });
box.add(framedImages);
function addBackgroundImageCase(borderWidth, borderRadius, width, height, extra) {
let imageBox = new St.BoxLayout({ style_class: 'background-image',
style: 'border: ' + borderWidth + 'px solid #8b8b8b;'
+ 'border-radius: ' + borderRadius + 'px;'
+ 'width: ' + width + 'px;'
+ 'height: ' + height + 'px;'
+ extra });
framedImages.add(imageBox, { x_fill: false, y_fill: false } );
}
addBackgroundImageCase (0, 0, 32, 32, 'background-position: 2px 5px');
addBackgroundImageCase (0, 0, 16, 16, '-st-background-image-shadow: 1px 1px 4px 0px rgba(0,0,0,0.5); background-color: rgba(0,0,0,0)');
addBackgroundImageCase (0, 5, 32, 32, '-st-background-image-shadow: 0px 0px 0px 0px rgba(0,0,0,0.5);');
addBackgroundImageCase (2, 5, 32, 32, '-st-background-image-shadow: 0px 2px 0px 0px rgba(0,255,0,0.5);');
addBackgroundImageCase (5, 2, 32, 32, '-st-background-image-shadow: 2px 0px 0px 0px rgba(0,0,255,0.5);');
addBackgroundImageCase (5, 20, 32, 32, '-st-background-image-shadow: 0px 0px 4px 0px rgba(255,0,0,0.5);');
addBackgroundImageCase (0, 5, 48, 48, '-st-background-image-shadow: 0px 0px 0px 4px rgba(0,0,0,0.5);');
addBackgroundImageCase (5, 5, 48, 48, '-st-background-image-shadow: 0px 0px 4px 4px rgba(0,0,0,0.5);');
addBackgroundImageCase (0, 5, 64, 64, '-st-background-image-shadow: -2px -2px 6px 0px rgba(0,0,0,0.5);');
addBackgroundImageCase (5, 5, 64, 64, '-st-background-image-shadow: -2px -2px 0px 6px rgba(0,0,0,0.5);');
addBackgroundImageCase (0, 5, 32, 32, 'background-position: 2px 5px');
UI.main(stage);
}
addBackgroundImageCase (0, 0, 32, 32, 'background-position: 2px 5px');
addBackgroundImageCase (0, 0, 16, 16, '-st-background-image-shadow: 1px 1px 4px 0px rgba(0,0,0,0.5); background-color: rgba(0,0,0,0)');
addBackgroundImageCase (0, 5, 32, 32, '-st-background-image-shadow: 0px 0px 0px 0px rgba(0,0,0,0.5);');
addBackgroundImageCase (2, 5, 32, 32, '-st-background-image-shadow: 0px 2px 0px 0px rgba(0,255,0,0.5);');
addBackgroundImageCase (5, 2, 32, 32, '-st-background-image-shadow: 2px 0px 0px 0px rgba(0,0,255,0.5);');
addBackgroundImageCase (5, 20, 32, 32, '-st-background-image-shadow: 0px 0px 4px 0px rgba(255,0,0,0.5);');
addBackgroundImageCase (0, 5, 48, 48, '-st-background-image-shadow: 0px 0px 0px 4px rgba(0,0,0,0.5);');
addBackgroundImageCase (5, 5, 48, 48, '-st-background-image-shadow: 0px 0px 4px 4px rgba(0,0,0,0.5);');
addBackgroundImageCase (0, 5, 64, 64, '-st-background-image-shadow: -2px -2px 6px 0px rgba(0,0,0,0.5);');
addBackgroundImageCase (5, 5, 64, 64, '-st-background-image-shadow: -2px -2px 0px 6px rgba(0,0,0,0.5);');
addBackgroundImageCase (0, 5, 32, 32, 'background-position: 2px 5px');
stage.show();
Clutter.main();
stage.destroy();
test();

View File

@ -5,88 +5,87 @@ const St = imports.gi.St;
const UI = imports.testcommon.ui;
UI.init();
let stage = Clutter.Stage.get_default();
function test() {
let stage = new Clutter.Stage();
UI.init(stage);
let vbox = new St.BoxLayout({ vertical: true,
width: stage.width,
height: stage.height,
style: 'padding: 10px;'
+ 'spacing: 10px;' });
stage.add_actor(vbox);
let vbox = new St.BoxLayout({ vertical: true,
width: stage.width,
height: stage.height,
style: 'padding: 10px;'
+ 'spacing: 10px;' });
stage.add_actor(vbox);
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
let colored_boxes = new St.BoxLayout({ vertical: true,
width: 200,
height: 200,
style: 'border: 2px solid black;' });
vbox.add(colored_boxes, { x_fill: false,
x_align: St.Align.MIDDLE });
let colored_boxes = new St.BoxLayout({ vertical: true,
width: 200,
height: 200,
style: 'border: 2px solid black;' });
vbox.add(colored_boxes, { x_fill: false,
x_align: St.Align.MIDDLE });
let b2 = new St.BoxLayout({ style: 'border: 2px solid #666666' });
colored_boxes.add(b2, { expand: true });
let b2 = new St.BoxLayout({ style: 'border: 2px solid #666666' });
colored_boxes.add(b2, { expand: true });
b2.add(new St.Label({ text: "Expand",
style: 'border: 1px solid #aaaaaa; '
+ 'background: #ffeecc' }),
{ expand: true });
b2.add(new St.Label({ text: "Expand\nNo Fill",
style: 'border: 1px solid #aaaaaa; '
+ 'background: #ccffaa' }),
{ expand: true,
x_fill: false,
x_align: St.Align.MIDDLE,
y_fill: false,
y_align: St.Align.MIDDLE });
b2.add(new St.Label({ text: "Expand",
style: 'border: 1px solid #aaaaaa; '
+ 'background: #ffeecc' }),
{ expand: true });
b2.add(new St.Label({ text: "Expand\nNo Fill",
style: 'border: 1px solid #aaaaaa; '
+ 'background: #ccffaa' }),
{ expand: true,
x_fill: false,
x_align: St.Align.MIDDLE,
y_fill: false,
y_align: St.Align.MIDDLE });
colored_boxes.add(new St.Label({ text: "Default",
style: 'border: 1px solid #aaaaaa; '
+ 'background: #cceeff' }));
colored_boxes.add(new St.Label({ text: "Default",
style: 'border: 1px solid #aaaaaa; '
+ 'background: #cceeff' }));
b2.add(new St.Label({ x: 50,
y: 50,
text: "Fixed",
style: 'border: 1px solid #aaaaaa;'
+ 'background: #ffffcc' }));
b2.add(new St.Label({ x: 50,
y: 50,
text: "Fixed",
style: 'border: 1px solid #aaaaaa;'
+ 'background: #ffffcc' }));
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
function createCollapsableBox(width) {
let b = new St.BoxLayout({ width: width,
style: 'border: 1px solid black;'
+ 'font: 13px Sans;' });
b.add(new St.Label({ text: "Very Very Very Long",
style: 'background: #ffaacc;'
+ 'padding: 5px; '
+ 'border: 1px solid #666666;' }),
{ expand: true });
b.add(new St.Label({ text: "Very Very Long",
style: 'background: #ffeecc; '
+ 'padding: 5px; '
+ 'border: 1px solid #666666;' }),
{ expand: true });
b.add(new St.Label({ text: "Very Long",
style: 'background: #ccffaa; '
+ 'padding: 5px; '
+ 'border: 1px solid #666666;' }),
{ expand: true });
b.add(new St.Label({ text: "Short",
style: 'background: #cceeff; '
+ 'padding: 5px; '
+ 'border: 1px solid #666666;' }),
{ expand: true });
function createCollapsableBox(width) {
let b = new St.BoxLayout({ width: width,
style: 'border: 1px solid black;'
+ 'font: 13px Sans;' });
b.add(new St.Label({ text: "Very Very Very Long",
style: 'background: #ffaacc;'
+ 'padding: 5px; '
+ 'border: 1px solid #666666;' }),
{ expand: true });
b.add(new St.Label({ text: "Very Very Long",
style: 'background: #ffeecc; '
+ 'padding: 5px; '
+ 'border: 1px solid #666666;' }),
{ expand: true });
b.add(new St.Label({ text: "Very Long",
style: 'background: #ccffaa; '
+ 'padding: 5px; '
+ 'border: 1px solid #666666;' }),
{ expand: true });
b.add(new St.Label({ text: "Short",
style: 'background: #cceeff; '
+ 'padding: 5px; '
+ 'border: 1px solid #666666;' }),
{ expand: true });
return b;
return b;
}
for (let width = 200; width <= 500; width += 60 ) {
vbox.add(createCollapsableBox (width),
{ x_fill: false,
x_align: St.Align.MIDDLE });
}
UI.main(stage);
}
for (let width = 200; width <= 500; width += 60 ) {
vbox.add(createCollapsableBox (width),
{ x_fill: false,
x_align: St.Align.MIDDLE });
}
////////////////////////////////////////////////////////////////////////////////
stage.show();
Clutter.main();

View File

@ -5,54 +5,53 @@ const St = imports.gi.St;
const UI = imports.testcommon.ui;
UI.init();
let stage = Clutter.Stage.get_default();
stage.width = 640;
stage.height = 480;
function test() {
let stage = new Clutter.Stage({ width: 640, height: 480 });
UI.init(stage);
let vbox = new St.BoxLayout({ width: stage.width,
height: stage.height,
style: 'background: #ffee88;' });
stage.add_actor(vbox);
let vbox = new St.BoxLayout({ width: stage.width,
height: stage.height,
style: 'background: #ffee88;' });
stage.add_actor(vbox);
let scroll = new St.ScrollView();
vbox.add(scroll, { expand: true });
let scroll = new St.ScrollView();
vbox.add(scroll, { expand: true });
let box = new St.BoxLayout({ vertical: true,
style: 'padding: 10px;'
+ 'spacing: 20px;' });
scroll.add_actor(box);
let box = new St.BoxLayout({ vertical: true,
style: 'padding: 10px;'
+ 'spacing: 20px;' });
scroll.add_actor(box);
function addTestCase(inset, offsetX, offsetY, blur, spread) {
let shadowStyle = 'box-shadow: ' + (inset ? 'inset ' : '') +
offsetX + 'px ' + offsetY + 'px ' + blur + 'px ' +
(spread > 0 ? (' ' + spread + 'px ') : '') +
'rgba(0,0,0,0.5);';
let label = new St.Label({ style: 'border: 4px solid black;' +
'border-radius: 5px;' +
'background-color: white; ' +
'padding: 5px;' +
shadowStyle,
text: shadowStyle });
box.add(label, { x_fill: false, y_fill: false } );
function addTestCase(inset, offsetX, offsetY, blur, spread) {
let shadowStyle = 'box-shadow: ' + (inset ? 'inset ' : '') +
offsetX + 'px ' + offsetY + 'px ' + blur + 'px ' +
(spread > 0 ? (' ' + spread + 'px ') : '') +
'rgba(0,0,0,0.5);';
let label = new St.Label({ style: 'border: 4px solid black;' +
'border-radius: 5px;' +
'background-color: white; ' +
'padding: 5px;' +
shadowStyle,
text: shadowStyle });
box.add(label, { x_fill: false, y_fill: false } );
}
addTestCase (false, 3, 4, 0, 0);
addTestCase (false, 3, 4, 0, 4);
addTestCase (false, 3, 4, 4, 0);
addTestCase (false, 3, 4, 4, 4);
addTestCase (false, -3, -4, 4, 0);
addTestCase (false, 0, 0, 0, 4);
addTestCase (false, 0, 0, 4, 0);
addTestCase (true, 3, 4, 0, 0);
addTestCase (true, 3, 4, 0, 4);
addTestCase (true, 3, 4, 4, 0);
addTestCase (true, 3, 4, 4, 4);
addTestCase (true, -3, -4, 4, 0);
addTestCase (true, 0, 0, 0, 4);
addTestCase (true, 0, 0, 4, 0);
UI.main(stage);
}
addTestCase (false, 3, 4, 0, 0);
addTestCase (false, 3, 4, 0, 4);
addTestCase (false, 3, 4, 4, 0);
addTestCase (false, 3, 4, 4, 4);
addTestCase (false, -3, -4, 4, 0);
addTestCase (false, 0, 0, 0, 4);
addTestCase (false, 0, 0, 4, 0);
addTestCase (true, 3, 4, 0, 0);
addTestCase (true, 3, 4, 0, 4);
addTestCase (true, 3, 4, 4, 0);
addTestCase (true, 3, 4, 4, 4);
addTestCase (true, -3, -4, 4, 0);
addTestCase (true, 0, 0, 0, 4);
addTestCase (true, 0, 0, 4, 0);
stage.show();
Clutter.main();
stage.destroy();
test();

View File

@ -7,23 +7,22 @@ const St = imports.gi.St;
const Calendar = imports.ui.calendar;
const UI = imports.testcommon.ui;
UI.init();
let stage = Clutter.Stage.get_default();
stage.width = stage.height = 400;
stage.show();
function test() {
let stage = new Clutter.Stage({ width: 400, height: 400 });
UI.init(stage);
let vbox = new St.BoxLayout({ vertical: true,
width: stage.width,
height: stage.height,
style: 'padding: 10px; spacing: 10px; font: 15px sans-serif;' });
stage.add_actor(vbox);
let vbox = new St.BoxLayout({ vertical: true,
width: stage.width,
height: stage.height,
style: 'padding: 10px; spacing: 10px; font: 15px sans-serif;' });
stage.add_actor(vbox);
let calendar = new Calendar.Calendar();
vbox.add(calendar.actor,
{ expand: true,
x_fill: false, x_align: St.Align.MIDDLE,
y_fill: false, y_align: St.Align.START });
let calendar = new Calendar.Calendar();
vbox.add(calendar.actor,
{ expand: true,
x_fill: false, x_align: St.Align.MIDDLE,
y_fill: false, y_align: St.Align.START });
stage.show();
Clutter.main();
stage.destroy();
UI.main(stage);
}
test();

View File

@ -5,35 +5,37 @@ const St = imports.gi.St;
const UI = imports.testcommon.ui;
UI.init();
let stage = Clutter.Stage.get_default();
function test() {
let stage = new Clutter.Stage();
UI.init(stage);
let b = new St.BoxLayout({ vertical: true,
width: stage.width,
height: stage.height });
stage.add_actor(b);
let b = new St.BoxLayout({ vertical: true,
width: stage.width,
height: stage.height });
stage.add_actor(b);
let t;
let t;
t = new St.Label({ "text": "Bold", style_class: "bold" });
b.add(t);
t = new St.Label({ "text": "Monospace", style_class: "monospace" });
b.add(t);
t = new St.Label({ "text": "Italic", style_class: "italic" });
b.add(t);
t = new St.Label({ "text": "Bold Italic", style_class: "bold italic" });
b.add(t);
t = new St.Label({ "text": "Big Italic", style_class: "big italic" });
b.add(t);
t = new St.Label({ "text": "Big Bold", style_class: "big bold" });
b.add(t);
t = new St.Label({ "text": "Bold", style_class: "bold" });
b.add(t);
t = new St.Label({ "text": "Monospace", style_class: "monospace" });
b.add(t);
t = new St.Label({ "text": "Italic", style_class: "italic" });
b.add(t);
t = new St.Label({ "text": "Bold Italic", style_class: "bold italic" });
b.add(t);
t = new St.Label({ "text": "Big Italic", style_class: "big italic" });
b.add(t);
t = new St.Label({ "text": "Big Bold", style_class: "big bold" });
b.add(t);
let b2 = new St.BoxLayout({ vertical: true, style_class: "monospace" });
b.add(b2);
t = new St.Label({ "text": "Big Monospace", style_class: "big" });
b2.add(t);
t = new St.Label({ "text": "Italic Monospace", style_class: "italic" });
b2.add(t);
let b2 = new St.BoxLayout({ vertical: true, style_class: "monospace" });
b.add(b2);
t = new St.Label({ "text": "Big Monospace", style_class: "big" });
b2.add(t);
t = new St.Label({ "text": "Italic Monospace", style_class: "italic" });
b2.add(t);
stage.show();
Clutter.main();
UI.main(stage);
}
test();

View File

@ -1,32 +1,28 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const St = imports.gi.St;
const Calendar = imports.ui.calendar;
const UI = imports.testcommon.ui;
Gtk.init(null, null);
function test() {
let stage = new Clutter.Stage({ width: 400, height: 400 });
UI.init(stage);
UI.init();
let stage = Clutter.Stage.get_default();
stage.width = stage.height = 400;
stage.show();
let vbox = new St.BoxLayout({ vertical: true,
width: stage.width,
height: stage.height,
style: 'padding: 10px; spacing: 10px; font: 15px sans-serif;' });
stage.add_actor(vbox);
let vbox = new St.BoxLayout({ vertical: true,
width: stage.width,
height: stage.height,
style: 'padding: 10px; spacing: 10px; font: 15px sans-serif;' });
stage.add_actor(vbox);
let entry = new St.Entry({ style: 'border: 1px solid black;' });
vbox.add(entry,
{ expand: true,
y_fill: false, y_align: St.Align.MIDDLE });
entry.grab_key_focus();
let entry = new St.Entry({ style: 'border: 1px solid black;' });
vbox.add(entry,
{ expand: true,
y_fill: false, y_align: St.Align.MIDDLE });
entry.grab_key_focus();
stage.show();
Clutter.main();
stage.destroy();
UI.main(stage);
}
test();

View File

@ -5,84 +5,83 @@ const St = imports.gi.St;
const UI = imports.testcommon.ui;
UI.init();
let stage = Clutter.Stage.get_default();
function test() {
let stage = new Clutter.Stage();
UI.init(stage);
stage.width = 400;
stage.height = 700;
let b = new St.BoxLayout({ vertical: true,
width: stage.width,
height: stage.height });
stage.add_actor(b);
let b = new St.BoxLayout({ vertical: true,
width: stage.width,
height: stage.height });
stage.add_actor(b);
function addTest(label, icon_props) {
if (b.get_children().length > 0)
b.add (new St.BoxLayout({ style: 'background: #cccccc; border: 10px transparent white; height: 1px; ' }));
function addTest(label, icon_props) {
if (b.get_children().length > 0)
b.add (new St.BoxLayout({ style: 'background: #cccccc; border: 10px transparent white; height: 1px; ' }));
let hb = new St.BoxLayout({ vertical: false,
style: 'spacing: 10px;' });
let hb = new St.BoxLayout({ vertical: false,
style: 'spacing: 10px;' });
hb.add(new St.Label({ text: label }), { y_fill: false });
hb.add(new St.Icon(icon_props));
hb.add(new St.Label({ text: label }), { y_fill: false });
hb.add(new St.Icon(icon_props));
b.add(hb);
}
addTest("Symbolic",
{ icon_name: 'battery-full',
icon_type: St.IconType.SYMBOLIC,
icon_size: 48 });
addTest("Full color",
{ icon_name: 'battery-full',
icon_type: St.IconType.FULLCOLOR,
icon_size: 48 });
addTest("Default size",
{ icon_name: 'battery-full',
icon_type: St.IconType.SYMBOLIC });
addTest("Size set by property",
{ icon_name: 'battery-full',
icon_type: St.IconType.SYMBOLIC,
icon_size: 32 });
addTest("Size set by style",
{ icon_name: 'battery-full',
icon_type: St.IconType.SYMBOLIC,
style: 'icon-size: 1em;' });
addTest("16px icon in 48px icon widget",
{ icon_name: 'battery-full',
icon_type: St.IconType.SYMBOLIC,
style: 'icon-size: 16px; width: 48px; height: 48px; border: 1px solid black;' });
function iconRow(icons, box_style) {
let hb = new St.BoxLayout({ vertical: false, style: box_style });
for each (let iconName in icons) {
hb.add(new St.Icon({ icon_name: iconName,
icon_type: St.IconType.SYMBOLIC,
icon_size: 48 }));
b.add(hb);
}
b.add(hb);
addTest("Symbolic",
{ icon_name: 'battery-full',
icon_type: St.IconType.SYMBOLIC,
icon_size: 48 });
addTest("Full color",
{ icon_name: 'battery-full',
icon_type: St.IconType.FULLCOLOR,
icon_size: 48 });
addTest("Default size",
{ icon_name: 'battery-full',
icon_type: St.IconType.SYMBOLIC });
addTest("Size set by property",
{ icon_name: 'battery-full',
icon_type: St.IconType.SYMBOLIC,
icon_size: 32 });
addTest("Size set by style",
{ icon_name: 'battery-full',
icon_type: St.IconType.SYMBOLIC,
style: 'icon-size: 1em;' });
addTest("16px icon in 48px icon widget",
{ icon_name: 'battery-full',
icon_type: St.IconType.SYMBOLIC,
style: 'icon-size: 16px; width: 48px; height: 48px; border: 1px solid black;' });
function iconRow(icons, box_style) {
let hb = new St.BoxLayout({ vertical: false, style: box_style });
for each (let iconName in icons) {
hb.add(new St.Icon({ icon_name: iconName,
icon_type: St.IconType.SYMBOLIC,
icon_size: 48 }));
}
b.add(hb);
}
let normalCss = 'background: white; color: black; padding: 10px 10px;';
let reversedCss = 'background: black; color: white; warning-color: #ffcc00; error-color: #ff0000; padding: 10px 10px;';
let batteryIcons = ['battery-full-charging',
'battery-full',
'battery-good',
'battery-low',
'battery-caution' ];
let volumeIcons = ['audio-volume-high',
'audio-volume-medium',
'audio-volume-low',
'audio-volume-muted' ];
iconRow(batteryIcons, normalCss);
iconRow(batteryIcons, reversedCss);
iconRow(volumeIcons, normalCss);
iconRow(volumeIcons, reversedCss);
UI.main(stage);
}
let normalCss = 'background: white; color: black; padding: 10px 10px;';
let reversedCss = 'background: black; color: white; warning-color: #ffcc00; error-color: #ff0000; padding: 10px 10px;';
let batteryIcons = ['battery-full-charging',
'battery-full',
'battery-good',
'battery-low',
'battery-caution' ];
let volumeIcons = ['audio-volume-high',
'audio-volume-medium',
'audio-volume-low',
'audio-volume-muted' ];
iconRow(batteryIcons, normalCss);
iconRow(batteryIcons, reversedCss);
iconRow(volumeIcons, normalCss);
iconRow(volumeIcons, reversedCss);
stage.show();
Clutter.main();
test();

View File

@ -5,43 +5,43 @@ const St = imports.gi.St;
const UI = imports.testcommon.ui;
UI.init();
let stage = Clutter.Stage.get_default();
function test() {
let stage = new Clutter.Stage();
UI.init(stage);
let vbox = new St.BoxLayout({ vertical: true,
width: stage.width,
height: stage.height });
stage.add_actor(vbox);
let vbox = new St.BoxLayout({ vertical: true,
width: stage.width,
height: stage.height });
stage.add_actor(vbox);
let hbox = new St.BoxLayout({ style: 'spacing: 12px;' });
vbox.add(hbox);
let hbox = new St.BoxLayout({ style: 'spacing: 12px;' });
vbox.add(hbox);
let text = new St.Label({ text: "Styled Text" });
vbox.add (text);
let text = new St.Label({ text: "Styled Text" });
vbox.add (text);
let size = 24;
function update_size() {
text.style = 'font-size: ' + size + 'pt';
let size = 24;
function update_size() {
text.style = 'font-size: ' + size + 'pt';
}
update_size();
let button;
button = new St.Button ({ label: 'Smaller', style_class: 'push-button' });
hbox.add (button);
button.connect('clicked', function() {
size /= 1.2;
update_size ();
});
button = new St.Button ({ label: 'Bigger', style_class: 'push-button' });
hbox.add (button);
button.connect('clicked', function() {
size *= 1.2;
update_size ();
});
UI.main(stage);
}
update_size();
let button;
button = new St.Button ({ label: 'Smaller', style_class: 'push-button' });
hbox.add (button);
button.connect('clicked', function() {
size /= 1.2;
update_size ();
});
button = new St.Button ({ label: 'Bigger', style_class: 'push-button' });
hbox.add (button);
button.connect('clicked', function() {
size *= 1.2;
update_size ();
});
stage.show();
Clutter.main();
stage.destroy();
test();

View File

@ -251,140 +251,139 @@ SizingIllustrator.prototype = {
/****************************************************************************/
UI.init();
let stage = Clutter.Stage.get_default();
stage.width = 600;
stage.height = 600;
function test() {
let stage = new Clutter.Stage({ width: 600, height: 600 });
UI.init(stage);
let mainBox = new St.BoxLayout({ width: stage.width,
height: stage.height,
vertical: true,
style: 'padding: 10px;'
+ 'spacing: 5px;'
+ 'font: 16px sans-serif;'
+ 'background: black;'
+ 'color: white;' });
stage.add_actor(mainBox);
let mainBox = new St.BoxLayout({ width: stage.width,
height: stage.height,
vertical: true,
style: 'padding: 10px;'
+ 'spacing: 5px;'
+ 'font: 16px sans-serif;'
+ 'background: black;'
+ 'color: white;' });
stage.add_actor(mainBox);
const DOCS = 'Red lines represent minimum size, blue lines natural size. Drag yellow handle to resize ScrollView. Click on options to change.';
const DOCS = 'Red lines represent minimum size, blue lines natural size. Drag yellow handle to resize ScrollView. Click on options to change.';
let docsLabel = new St.Label({ text: DOCS });
docsLabel.clutter_text.line_wrap = true;
mainBox.add(docsLabel);
let docsLabel = new St.Label({ text: DOCS });
docsLabel.clutter_text.line_wrap = true;
mainBox.add(docsLabel);
let bin = new St.Bin({ x_fill: true, y_fill: true, style: 'border: 2px solid #666666;' });
mainBox.add(bin, { x_fill: true, y_fill: true, expand: true });
let bin = new St.Bin({ x_fill: true, y_fill: true, style: 'border: 2px solid #666666;' });
mainBox.add(bin, { x_fill: true, y_fill: true, expand: true });
let illustrator = new SizingIllustrator();
bin.add_actor(illustrator.actor);
let illustrator = new SizingIllustrator();
bin.add_actor(illustrator.actor);
let scrollView = new St.ScrollView();
illustrator.add(scrollView);
let scrollView = new St.ScrollView();
illustrator.add(scrollView);
let box = new St.BoxLayout({ vertical: true });
scrollView.add_actor(box);
let box = new St.BoxLayout({ vertical: true });
scrollView.add_actor(box);
let flowedBoxes = new FlowedBoxes();
box.add(flowedBoxes.actor, { expand: false, x_fill: true, y_fill: true });
let flowedBoxes = new FlowedBoxes();
box.add(flowedBoxes.actor, { expand: false, x_fill: true, y_fill: true });
let policyBox = new St.BoxLayout({ vertical: false });
mainBox.add(policyBox);
let policyBox = new St.BoxLayout({ vertical: false });
mainBox.add(policyBox);
policyBox.add(new St.Label({ text: 'Horizontal Policy: ' }));
let hpolicy = new St.Button({ label: 'AUTOMATIC', style: 'text-decoration: underline; color: #4444ff;' });
policyBox.add(hpolicy);
policyBox.add(new St.Label({ text: 'Horizontal Policy: ' }));
let hpolicy = new St.Button({ label: 'AUTOMATIC', style: 'text-decoration: underline; color: #4444ff;' });
policyBox.add(hpolicy);
let spacer = new St.Bin();
policyBox.add(spacer, { expand: true });
let spacer = new St.Bin();
policyBox.add(spacer, { expand: true });
policyBox.add(new St.Label({ text: 'Vertical Policy: '}));
let vpolicy = new St.Button({ label: 'AUTOMATIC', style: 'text-decoration: underline; color: #4444ff;' });
policyBox.add(vpolicy);
policyBox.add(new St.Label({ text: 'Vertical Policy: '}));
let vpolicy = new St.Button({ label: 'AUTOMATIC', style: 'text-decoration: underline; color: #4444ff;' });
policyBox.add(vpolicy);
function togglePolicy(button) {
switch(button.label) {
case 'AUTOMATIC':
button.label = 'ALWAYS';
break;
case 'ALWAYS':
button.label = 'NEVER';
break;
case 'NEVER':
button.label = 'AUTOMATIC';
break;
function togglePolicy(button) {
switch(button.label) {
case 'AUTOMATIC':
button.label = 'ALWAYS';
break;
case 'ALWAYS':
button.label = 'NEVER';
break;
case 'NEVER':
button.label = 'AUTOMATIC';
break;
}
scrollView.set_policy(Gtk.PolicyType[hpolicy.label], Gtk.PolicyType[vpolicy.label]);
}
scrollView.set_policy(Gtk.PolicyType[hpolicy.label], Gtk.PolicyType[vpolicy.label]);
}
hpolicy.connect('clicked', function() { togglePolicy(hpolicy); });
vpolicy.connect('clicked', function() { togglePolicy(vpolicy); });
hpolicy.connect('clicked', function() { togglePolicy(hpolicy); });
vpolicy.connect('clicked', function() { togglePolicy(vpolicy); });
let fadeBox = new St.BoxLayout({ vertical: false });
mainBox.add(fadeBox);
let fadeBox = new St.BoxLayout({ vertical: false });
mainBox.add(fadeBox);
spacer = new St.Bin();
fadeBox.add(spacer, { expand: true });
spacer = new St.Bin();
fadeBox.add(spacer, { expand: true });
fadeBox.add(new St.Label({ text: 'Padding: '}));
let paddingButton = new St.Button({ label: 'No', style: 'text-decoration: underline; color: #4444ff;padding-right:3px;' });
fadeBox.add(paddingButton);
fadeBox.add(new St.Label({ text: 'Padding: '}));
let paddingButton = new St.Button({ label: 'No', style: 'text-decoration: underline; color: #4444ff;padding-right:3px;' });
fadeBox.add(paddingButton);
fadeBox.add(new St.Label({ text: 'Borders: '}));
let borderButton = new St.Button({ label: 'No', style: 'text-decoration: underline; color: #4444ff;padding-right:3px;' });
fadeBox.add(borderButton);
fadeBox.add(new St.Label({ text: 'Borders: '}));
let borderButton = new St.Button({ label: 'No', style: 'text-decoration: underline; color: #4444ff;padding-right:3px;' });
fadeBox.add(borderButton);
fadeBox.add(new St.Label({ text: 'Vertical Fade: '}));
let vfade = new St.Button({ label: 'No', style: 'text-decoration: underline; color: #4444ff;' });
fadeBox.add(vfade);
fadeBox.add(new St.Label({ text: 'Vertical Fade: '}));
let vfade = new St.Button({ label: 'No', style: 'text-decoration: underline; color: #4444ff;' });
fadeBox.add(vfade);
function togglePadding(button) {
switch(button.label) {
case 'No':
button.label = 'Yes';
break;
case 'Yes':
button.label = 'No';
break;
function togglePadding(button) {
switch(button.label) {
case 'No':
button.label = 'Yes';
break;
case 'Yes':
button.label = 'No';
break;
}
if (scrollView.style == null)
scrollView.style = (button.label == 'Yes' ? 'padding: 10px;' : 'padding: 0;');
else
scrollView.style += (button.label == 'Yes' ? 'padding: 10px;' : 'padding: 0;');
}
if (scrollView.style == null)
scrollView.style = (button.label == 'Yes' ? 'padding: 10px;' : 'padding: 0;');
else
scrollView.style += (button.label == 'Yes' ? 'padding: 10px;' : 'padding: 0;');
}
paddingButton.connect('clicked', function() { togglePadding(paddingButton); });
paddingButton.connect('clicked', function() { togglePadding(paddingButton); });
function toggleBorders(button) {
switch(button.label) {
case 'No':
button.label = 'Yes';
break;
case 'Yes':
button.label = 'No';
break;
function toggleBorders(button) {
switch(button.label) {
case 'No':
button.label = 'Yes';
break;
case 'Yes':
button.label = 'No';
break;
}
if (scrollView.style == null)
scrollView.style = (button.label == 'Yes' ? 'border: 2px solid red;' : 'border: 0;');
else
scrollView.style += (button.label == 'Yes' ? 'border: 2px solid red;' : 'border: 0;');
}
if (scrollView.style == null)
scrollView.style = (button.label == 'Yes' ? 'border: 2px solid red;' : 'border: 0;');
else
scrollView.style += (button.label == 'Yes' ? 'border: 2px solid red;' : 'border: 0;');
}
borderButton.connect('clicked', function() { toggleBorders(borderButton); });
borderButton.connect('clicked', function() { toggleBorders(borderButton); });
function toggleFade(button) {
switch(button.label) {
case 'No':
button.label = 'Yes';
break;
case 'Yes':
button.label = 'No';
break;
function toggleFade(button) {
switch(button.label) {
case 'No':
button.label = 'Yes';
break;
case 'Yes':
button.label = 'No';
break;
}
scrollView.set_style_class_name(button.label == 'Yes' ? 'vfade' : '');
}
scrollView.set_style_class_name(button.label == 'Yes' ? 'vfade' : '');
vfade.connect('clicked', function() { toggleFade(vfade); });
UI.main(stage);
}
vfade.connect('clicked', function() { toggleFade(vfade); });
stage.show();
Clutter.main();
stage.destroy();
test();

View File

@ -6,47 +6,48 @@ const St = imports.gi.St;
const UI = imports.testcommon.ui;
UI.init();
let stage = Clutter.Stage.get_default();
function test() {
let stage = new Clutter.Stage();
UI.init(stage);
let vbox = new St.BoxLayout({ vertical: true,
width: stage.width,
height: stage.height,
style: "padding: 10px;" });
stage.add_actor(vbox);
let vbox = new St.BoxLayout({ vertical: true,
width: stage.width,
height: stage.height,
style: "padding: 10px;" });
stage.add_actor(vbox);
let toggle = new St.Button({ label: 'Horizontal Scrolling',
toggle_mode: true });
vbox.add(toggle);
let toggle = new St.Button({ label: 'Horizontal Scrolling',
toggle_mode: true });
vbox.add(toggle);
let v = new St.ScrollView();
vbox.add(v, { expand: true });
let v = new St.ScrollView();
vbox.add(v, { expand: true });
toggle.connect('notify::checked', function () {
v.set_policy(toggle.checked ? Gtk.PolicyType.AUTOMATIC
: Gtk.PolicyType.NEVER,
Gtk.PolicyType.AUTOMATIC);
});
toggle.connect('notify::checked', function () {
v.set_policy(toggle.checked ? Gtk.PolicyType.AUTOMATIC
: Gtk.PolicyType.NEVER,
Gtk.PolicyType.AUTOMATIC);
});
let b = new St.BoxLayout({ vertical: true,
style: "border: 2px solid #880000; border-radius: 10px; padding: 0px 5px;" });
v.add_actor(b);
let b = new St.BoxLayout({ vertical: true,
style: "border: 2px solid #880000; border-radius: 10px; padding: 0px 5px;" });
v.add_actor(b);
let cc_a = "a".charCodeAt(0);
let s = "";
for (let i = 0; i < 26 * 3; i++) {
s += String.fromCharCode(cc_a + i % 26);
let cc_a = "a".charCodeAt(0);
let s = "";
for (let i = 0; i < 26 * 3; i++) {
s += String.fromCharCode(cc_a + i % 26);
let t = new St.Label({ text: s,
reactive: true });
let line = i + 1;
t.connect('button-press-event',
function() {
log("Click on line " + line);
});
b.add(t);
let t = new St.Label({ text: s,
reactive: true });
let line = i + 1;
t.connect('button-press-event',
function() {
log("Click on line " + line);
});
b.add(t);
}
UI.main(stage);
}
stage.show();
Clutter.main();
stage.destroy();
test();

View File

@ -5,52 +5,53 @@ const St = imports.gi.St;
const UI = imports.testcommon.ui;
UI.init();
let stage = Clutter.Stage.get_default();
stage.width = stage.height = 600;
function test() {
let stage = new Clutter.Stage({ width: 600, height: 600 });
UI.init(stage);
let vbox = new St.BoxLayout({ vertical: true,
width: stage.width,
height: stage.height,
style: 'padding: 10px; '
+ 'spacing: 10px;'
+ 'font: 15px sans-serif;' });
stage.add_actor(vbox);
let vbox = new St.BoxLayout({ vertical: true,
width: stage.width,
height: stage.height,
style: 'padding: 10px; '
+ 'spacing: 10px;'
+ 'font: 15px sans-serif;' });
stage.add_actor(vbox);
function L(text, color) {
return new St.Label({ text: text,
style: "background: " + color + ";"
+ "border: 1px solid rgba(0,0,0,0.5);"
+ "padding: 1em;" });
function L(text, color) {
return new St.Label({ text: text,
style: "background: " + color + ";"
+ "border: 1px solid rgba(0,0,0,0.5);"
+ "padding: 1em;" });
}
////////////////////////////////////////////////////////////////////////////////
let table = new St.Table({ style: "border: 10px solid #888888;"
+ "padding: 10px;"
+ "spacing-rows: 5px;"
+ "spacing-columns: 15px;" });
vbox.add(table, { expand: true });
table.add(L("1", "#ff0000"),
{ row: 0, col: 0, col_span: 3 });
table.add(L("2", "#00ff00"),
{ row: 1, col: 0, row_span: 2 });
table.add(L("3", "#0000ff"),
{ row: 1, col: 1,
x_expand: 0 });
table.add(L("4", "#ffff00"),
{ row: 1, col: 2,
y_expand: 0, y_fill: 0
});
table.add(L("5", "#ff00ff"),
{ row: 2, col: 1, x_expand: 0 });
table.add(L("6", "#00ffff"),
{ row: 2, col: 2,
x_expand: 0, x_fill: 0, x_align: St.Align.END,
y_expand: 0, y_fill: 0, y_align: St.Align.END });
////////////////////////////////////////////////////////////////////////////////
UI.main(stage);
}
////////////////////////////////////////////////////////////////////////////////
let table = new St.Table({ style: "border: 10px solid #888888;"
+ "padding: 10px;"
+ "spacing-rows: 5px;"
+ "spacing-columns: 15px;" });
vbox.add(table, { expand: true });
table.add(L("1", "#ff0000"),
{ row: 0, col: 0, col_span: 3 });
table.add(L("2", "#00ff00"),
{ row: 1, col: 0, row_span: 2 });
table.add(L("3", "#0000ff"),
{ row: 1, col: 1,
x_expand: 0 });
table.add(L("4", "#ffff00"),
{ row: 1, col: 2,
y_expand: 0, y_fill: 0
});
table.add(L("5", "#ff00ff"),
{ row: 2, col: 1, x_expand: 0 });
table.add(L("6", "#00ffff"),
{ row: 2, col: 2,
x_expand: 0, x_fill: 0, x_align: St.Align.END,
y_expand: 0, y_fill: 0, y_align: St.Align.END });
////////////////////////////////////////////////////////////////////////////////
stage.show();
Clutter.main();
test();

View File

@ -5,30 +5,32 @@ const St = imports.gi.St;
const UI = imports.testcommon.ui;
UI.init();
let stage = Clutter.Stage.get_default();
function test() {
let stage = new Clutter.Stage();
UI.init(stage);
let hbox = new St.BoxLayout({ name: 'transition-container',
reactive: true,
track_hover: true,
width: stage.width,
height: stage.height,
style: 'padding: 10px;'
+ 'spacing: 10px;' });
stage.add_actor(hbox);
let hbox = new St.BoxLayout({ name: 'transition-container',
reactive: true,
track_hover: true,
width: stage.width,
height: stage.height,
style: 'padding: 10px;'
+ 'spacing: 10px;' });
stage.add_actor(hbox);
for (let i = 0; i < 5; i ++) {
let label = new St.Label({ text: (i+1).toString(),
name: "label" + i,
style_class: 'transition-label',
reactive: true,
track_hover: true });
for (let i = 0; i < 5; i ++) {
let label = new St.Label({ text: (i+1).toString(),
name: "label" + i,
style_class: 'transition-label',
reactive: true,
track_hover: true });
hbox.add(label, { x_fill: false,
y_fill: false });
hbox.add(label, { x_fill: false,
y_fill: false });
}
////////////////////////////////////////////////////////////////////////////////
UI.main(stage);
}
////////////////////////////////////////////////////////////////////////////////
stage.show();
Clutter.main();
test();

View File

@ -3,16 +3,21 @@
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
const Environment = imports.ui.environment;
function init() {
function init(stage) {
Environment.init();
let stage = Clutter.Stage.get_default();
let context = St.ThemeContext.get_for_stage (stage);
let context = St.ThemeContext.get_for_stage(stage);
let stylesheetPath = GLib.getenv("GNOME_SHELL_TESTSDIR") + "/testcommon/test.css";
let theme = new St.Theme ({ application_stylesheet: stylesheetPath });
context.set_theme (theme);
let theme = new St.Theme({ application_stylesheet: stylesheetPath });
context.set_theme(theme);
}
function main(stage) {
stage.show();
stage.connect('destroy', function() {
Clutter.main_quit();
});
Clutter.main();
}