Compare commits

...

943 Commits
osk ... 3.3.5

Author SHA1 Message Date
ab29ce872a Bump version to 3.3.5
Updated NEWS
Require Mutter 3.3.5
2012-02-07 18:31:29 -05:00
57beb0ade1 data/Makefile.am: fix typo in EXTRA_DIST 2012-02-07 18:31:29 -05:00
e3d0b6f90f Add -Wno-error=deprecated declarations
Even with --enable-compile-warnings=error, avoid erroring out on deprecations
for the moment, since we are hitting many Clutter deprecations and some are
hard to fix.
2012-02-07 18:21:56 -05:00
e2aa954cb5 st: Fix typo in doc comment 2012-02-08 00:01:12 +01:00
05c285a945 st: Shut up a compiler warning
Remove an unused variable to make GCC happy.
2012-02-08 00:01:12 +01:00
27b34992c6 iconGrid: Don't enter an infinite loop
If both spacing and -shell-grid-item-size are 0, as they would be with nothing
setting them, we enter an infinite loop where we try to compute the layout.
Avoid the situation entirely by defaulting -shell-grid-item-size to a sane
value instead of 0.

https://bugzilla.gnome.org/show_bug.cgi?id=662747
2012-02-07 17:40:22 -05:00
4886238761 Updated POTFILES.in 2012-02-07 23:14:34 +01:00
42d46aed90 po: Add extensionPrefs/main.js to the localized file set 2012-02-07 16:49:19 -05:00
a622aba7eb extensionUtils: Create and allow access to a new "extension" object
The "extension" object is what I previously called the "helper" object.
It contains the extension importer object as well as the metadata object.
Things that were previously added on to the metadata (state, path, dir, etc.)
are now part of this new "extension" object.

With the new importer changes brought on by the extension prefs tool,
extensions are left without a way to import submodules at the global scope,
which would make them rely on techniques like:

  var MySubModule;

  function init(meta) {
      MySubModule = meta.importer.mySubModule;
  }

That is, there's now a lot more meaningless boilerplate that nobody wants
to write and nobody wants to reivew.

Let's solve this with a few clever hacks.
Allow extensions to get their current extension object with:

  let extension = imports.misc.extensionUtils.getCurrentExtension();

As such, extensions can now get their own extension object before the
'init' method is called, so they can import submodules or do other things
at the module scope:

  const MySubModule = extension.imports.mySubModule;
  const dataPath = GLib.build_filenamev([extension.path, 'awesome-data.json']);

https://bugzilla.gnome.org/show_bug.cgi?id=668429
2012-02-07 16:00:37 -05:00
831099cca5 browser-plugin: Provide new APIs for launching extension preferences
Add two new APIs, "launchExtensionPrefs" to let SweetTooth let the user
launch the extension preferences tool directly from the browser. To allow
SweetTooth to check if an extension can be configured, add a new key to
the 'metadata', 'hasPrefs', which is returned by the GetExtensionInfo/
ListExtensions DBus methods.

https://bugzilla.gnome.org/show_bug.cgi?id=668429
2012-02-07 16:00:37 -05:00
b8a54faf94 Add a new tool, 'gnome-shell-extension-prefs', which can configure extensions
A new tool, 'gnome-shell-extension-prefs' can load a new entry point from
extensions, 'prefs.js', which has an entry point to return a GTK+ widget.
This allows extensions to have their own preferences dialog, without each
extension needing to ship its own Python script and .desktop file.

https://bugzilla.gnome.org/show_bug.cgi?id=668429
2012-02-07 16:00:37 -05:00
80ff6ff797 Move a lot of miscellaneous code related to extensions into a new module
ExtensionUtils is a new module that has a lot of miscellaneous things related
to loading extensions and the extension system put into a place that does not
depend on Shell or St.

Note that this will break extensions that have with multiple files by replacing
the old uuid-based importer with an object directly on the meta object.

https://bugzilla.gnome.org/show_bug.cgi?id=668429
2012-02-07 16:00:37 -05:00
2f27b94757 extensionSystem: Fix an error related to extension importing
If an extension fails to import, we will pass the error object
to logExtensionError, which fails to pass it onto DBus as an
error object is not a string. To fix, convert the error object
to a string before passing it to logExtensionError.

https://bugzilla.gnome.org/show_bug.cgi?id=668429
2012-02-07 16:00:37 -05:00
b2f33e2895 Split off the extension importing stuff into a new library, 'ShellJS'
This allows us to create a separate utility to import things from
shell extensions that does not have the entire Shell stack built up

https://bugzilla.gnome.org/show_bug.cgi?id=668429
2012-02-07 16:00:37 -05:00
d1d4142052 Makefile.am: Use global substitutions
This allows us to make more than one of the same replacement in a .in file

https://bugzilla.gnome.org/show_bug.cgi?id=668429
2012-02-07 16:00:36 -05:00
46caf6d673 ShellApp: Fix warning with call to g_strv_length()
There is no implicit cast from const char * const * to char **.
2012-02-07 13:06:24 -05:00
2864c360bc theme: Fix highlight of panel buttons
More fallout from the background-size change ...
2012-02-07 18:58:59 +01:00
e5dfc6323a Updated Galician translations 2012-02-07 11:54:19 +01:00
5bc042ba6f Remove override of map/unmap
Clutter, since version 1.8, does The Right Thing™ inside the default
implementation of ClutterActor::map and ClutterActor::unmap, even for
non-container actors: it will iterate over the list of children and
map, or unmap, each one of them, respectively.

This means that the requirement to override map and unmap for composite
actors to map and unmap the internal children has been dropped.

https://bugzilla.gnome.org/show_bug.cgi?id=669239
2012-02-06 23:36:43 +00:00
c63fe5ee24 workspaceThumbnail: avoid bouncing of the drop placeholder above the first workspace
https://bugzilla.gnome.org/show_bug.cgi?id=664622
2012-02-06 11:16:28 -05:00
ee6bc33cea PopupSwitchMenuItem: allow toggling without closing the menu
Similar to what Gtk does, now toggling with Space does not close
the menu.

https://bugzilla.gnome.org/show_bug.cgi?id=664416
2012-02-06 16:49:15 +01:00
5c730dc53d st-theme-node-drawing: Remove possible subtexturing
Since our implementation of background-size is now CSS-compliant, we
do not need this subtexture hack that clips a "leak". The comment here
is also incorrect.

https://bugzilla.gnome.org/show_bug.cgi?id=633462
2012-02-06 08:01:33 -05:00
5a3de8d663 st-theme-node-drawing: Fix implementation of background-size
It seems that accidentally, two variables were swapped in one code path
of the background-size implementation, causing interesting but wrong
images for some elements.

https://bugzilla.gnome.org/show_bug.cgi?id=633462
2012-02-06 08:01:33 -05:00
fad88dd517 theme: More fallout from background-size
The app filter arrows and scroll handles should be at 100%, not
mapped to their container

https://bugzilla.gnome.org/show_bug.cgi?id=633462
2012-02-06 08:01:33 -05:00
39dd24310d Updated Galician translations 2012-02-06 02:52:16 +01:00
b13809d0c7 Updated Dutch translation 2012-02-04 17:13:10 +01:00
87559414a3 screenGrabber: Pass correct coordinates to glReadPixels
Pass x and y to glReadPixels rather then always 0, 0.

https://bugzilla.gnome.org/show_bug.cgi?id=669366
2012-02-04 17:08:28 +01:00
b5b5759829 Updated Dutch translation 2012-02-04 15:59:58 +01:00
d254e2e1f2 shell-global: Correct screenshot_window() after mutter changes
Recent mutter changes made MetaShapedTexture not a ClutterTexture,
but instead a special ClutterActor subclass that implemented the texture-y
bits itself. Use recently introduced API in MetaShapedTexture so that we can
get the raw texture data and spit it out as a PNG.

Use the new meta_shaped_texture_get_image() to get a window's texture data.
meta_shaped_texture_get_image() flattens the image against any mask it may
have, so a screenshot of it should look exactly as it does on the display.

https://bugzilla.gnome.org/show_bug.cgi?id=662486
2012-02-03 19:58:39 -05:00
458b0b22fc shell-util: Remove shell_breakpoint
A near identical function appears in gjs, in the "system" module:

    const System = imports.system;
    System.breakpoint();
2012-02-03 14:21:57 -05:00
cd30128af8 placeDisplay: Fix accidental swap of parameters
https://bugzilla.gnome.org/show_bug.cgi?id=669236
2012-02-02 15:31:46 -05:00
d61cdd8cea Updated Slovenian translation 2012-02-02 20:58:35 +01:00
0d0e545979 configure: fix help string for jhbuild-wrapper-script option 2012-02-02 08:26:44 -05:00
6c5e96c33a added Sinhala language to LINGUAS file 2012-02-01 15:53:10 +05:30
bae2359b54 added Sinhala translation 2012-02-01 15:46:43 +05:30
8cbbb456f0 mount-operation: set a max-width for the question subject label
Since the string can be arbitrairly long.

https://bugzilla.gnome.org/show_bug.cgi?id=665322
2012-02-01 00:57:19 -05:00
dfd39461cf Updated Spanish translation 2012-01-31 17:49:13 +01:00
60d8683ae7 ShellRecorder: drop frames to approximate the target framerate
Instead of adding every rendered frame into the recording, drop frames
and only buffer and record enough frames to match the target framerate.

Increase the default frame rate from 15 to 30, since now that we're
actually enforcing framerate, it's noticeable that 15fps is not smooth.

https://bugzilla.gnome.org/show_bug.cgi?id=669066
2012-01-31 10:47:19 -05:00
f2cc5cf152 ShellRecorder: drop frames to keep from running the user out of memory.
Once we're buffering more than 3/4's of the "half of memory" target
for total buffer usage, start dropping frames.

https://bugzilla.gnome.org/show_bug.cgi?id=669066
2012-01-31 10:47:19 -05:00
d4a26fbf4b ShellRecorder: improve the default pipeline
The default pipeline was fairly aggressive about quality, and could
be too expensive for some computers. Decrease the quality setting for
the vp8 codec from 10 to 8, and increase the speed setting from 2 to 6.

(Basically, quality affects the visual fidelity of the end result, while
speed affects how much CPU the encoder uses to get a high compression
ratio at that quality level.)

Remove videorate from the pipeline, since the GStreamer VP8 encoder can
handle variable-framerate streams. This means that we won't spend CPU
encoding duplicate frames added by videorate.

https://bugzilla.gnome.org/show_bug.cgi?id=669066
2012-01-31 10:47:19 -05:00
025784fd83 ShellRecorderSrc: Set as GST_FORMAT_TIME
We need to indicate that our GStreamer source produces timestamped
frames, instead of the default, which is to produce a stream of bytes.
This is needed for correctness, and to avoid warnings for some
pipelines.

https://bugzilla.gnome.org/show_bug.cgi?id=669066
2012-01-31 10:47:19 -05:00
4e89a5edde ShellScreenGrabber: grab the screen using pixel buffers
For the Intel drivers, using glReadPixels() to read into client-memory
directly from the frame buffer is much slower than creating a pixel
buffer, copying into that, and then mapping that for reading. On other
drivers, the two approaches are likely to be similar in speed. Create
a ShellScreenGrabber abstraction that uses pixel buffers if available.
Use that for screenshots and screen recording.

https://bugzilla.gnome.org/show_bug.cgi?id=669065
2012-01-31 10:47:19 -05:00
b3936ecadf user-menu: Don't change HIDDEN to EXTENDED_AWAY
When the session status changes to IDLE, we automatically adjust
the IM presence; however, we should treat HIDDEN the same as OFFLINE
and not change the presence.

https://bugzilla.gnome.org/show_bug.cgi?id=642408
2012-01-31 10:22:53 +01:00
2c9e6bb589 browser-plugin: Fix the browser plugin
commit 26991988cb broke the browser plugin
by trying to reference a uninitialized pointer and making the NPAPI retain
a NULL object.

https://bugzilla.gnome.org/show_bug.cgi?id=668517
2012-01-30 19:26:50 -05:00
73261a4a66 Updated Traditional Chinese translation(Hong Kong and Taiwan) 2012-01-30 19:30:53 +08:00
3d0dd38045 automountManager: Make sure sessionActive is initialized
Since the port to GDBus, sessionActive is only set on DBus name
owner changes; this means that it may end up not being initialized
at all, and therefore always evaluate to false.
Make sure that the property is always initialized on startup.

https://bugzilla.gnome.org/show_bug.cgi?id=668020
2012-01-30 00:09:56 +01:00
8bcbf3030f power: Fix icon updates
gnome-settings-daemon commit 07b1ed63016 removed the custom 'Changed'
DBus signal in favor of the standard 'PropertiesChanged' signal, so
use that instead to update the icon.

https://bugzilla.gnome.org/show_bug.cgi?id=667371
2012-01-29 23:21:01 +01:00
1bc7edc5d8 Updated Slovenian translation 2012-01-28 19:52:48 +01:00
0673720db9 Updated Norwegian bokmål translation 2012-01-28 15:53:43 +01:00
730a0d4c5a Updated Hebrew translation. 2012-01-28 11:25:20 +02:00
9147dee0de screenshot: use the correct offsets when calculating the window area
Use the correct clip offsets when taking the screenshot of a window, to
exclude possible invisible borders and to include the case where the
window doesn't have any frame itself.
2012-01-27 17:15:03 -05:00
12746a1949 modemManager: Fix fallout from GDBus port
We need to listen to DBus signals with the 'connectSignal' method, not
the Signals 'connect' method.
2012-01-27 14:59:32 -05:00
bdd65fe755 AppMenuButton: bind "can-focus" to "reactive"
When changing to an empty workspace we make the AppMenuButton invisible but
the user could still get to the menu using keyboard navigation.

https://bugzilla.gnome.org/show_bug.cgi?id=643867
2012-01-27 18:01:22 +00:00
21e2280825 AppMenuButton: only show the button if the target app is on the current ws
When the last window on a workspace is closed the focus goes to some other
window in another workspace which would cause us to show the AppMenuButton for
an application that isn't visible on the current empty workspace.

https://bugzilla.gnome.org/show_bug.cgi?id=643867
2012-01-27 16:34:52 +00:00
e9d2a429eb message-tray: Allow to switch between left/right click directly
Currently it is not possible to trigger the context menu while the
summary notification is opened (and vice versa). To actually trigger
the desired item, the user has to click again, which is annoying
without a good justification, so allow switching directly between
left/right click items.

https://bugzilla.gnome.org/show_bug.cgi?id=666197
2012-01-27 14:27:41 +01:00
b67dfb9edf Updated Spanish translation 2012-01-27 13:40:06 +01:00
55308917f9 popup-menu: Do not open empty menus
There is little point in showing an empty menu, so return early from
open() if the menu does not contain any items.

https://bugzilla.gnome.org/show_bug.cgi?id=643867
2012-01-27 13:32:43 +01:00
1b64b09532 a11y: StLabelAccessible needs to notify accessible-name change
https://bugzilla.gnome.org/show_bug.cgi?id=667376
2012-01-27 12:29:57 +01:00
74dd298891 a11y: Setting a name/label_actor for several items on the panel
https://bugzilla.gnome.org/show_bug.cgi?id=667376
2012-01-27 12:29:52 +01:00
5a85fc0e55 magnifier: Handle screen size changes
Update everything that depends on the screen size whenever it changes.

https://bugzilla.gnome.org/show_bug.cgi?id=667860
2012-01-26 15:14:48 +00:00
26991988cb browser-plugin: Refactor plugin_object_set_property, and fix a bug
If the user did "obj.onchanged = 1;" or passed another sort of invalid type,
then we would clear the old listener as well as throw an exception.

https://bugzilla.gnome.org/show_bug.cgi?id=668517
2012-01-26 05:13:21 -05:00
15563444cf browser-plugin: Fix callback for "onchange"
In the case that calling the listener fails, "result" may be uninitialized.
Sending NPAPI uninitialized memory is never a good idea.

https://bugzilla.gnome.org/show_bug.cgi?id=668517
2012-01-26 05:13:21 -05:00
3bcdba6e1d browser-plugin: Add a new "onshellrestart" API
This function is something the client sets and is called whenever the Shell's
DBus name is acquired.

https://bugzilla.gnome.org/show_bug.cgi?id=668517
2012-01-26 05:13:21 -05:00
ef56a78544 shell-dbus: factor screenshot callback into a separate function
Share the screenshot methods callback into a factored out function.

https://bugzilla.gnome.org/show_bug.cgi?id=668618
2012-01-25 19:20:09 -05:00
049a561466 screenshot: add a 'flash' boolean flag to screenshot methods
Add a flag to these methods that allows flashing the area of the
screenshot directly from the compositor.

https://bugzilla.gnome.org/show_bug.cgi?id=668618
2012-01-25 19:20:09 -05:00
b40b19997a shell: Use generic marshaller
https://bugzilla.gnome.org/show_bug.cgi?id=662152
2012-01-25 19:03:48 -05:00
46505a8314 etc: Use generic marshaller
https://bugzilla.gnome.org/show_bug.cgi?id=662152
2012-01-25 19:03:13 -05:00
78fb102002 st: Use generic marshaller
https://bugzilla.gnome.org/show_bug.cgi?id=662152
2012-01-25 19:03:01 -05:00
c6e924f788 browser-plugin: Fix leak in plugin_enable_extension
https://bugzilla.gnome.org/show_bug.cgi?id=668541
2012-01-25 19:02:08 -05:00
f6508b51a2 st-im-text: Guard against multiple dispose
This could cause warnings like "invalid (NULL) pointer instance"

https://bugzilla.gnome.org/show_bug.cgi?id=665000
2012-01-25 19:01:33 -05:00
b0ae596de8 contact-display: Don't show non-IM contacts as "offline"
Currently we display IM status information for every contact, falling
back to "offline" if the contact does not have an associated IM
account. Instead, don't show IM presence in this case.

https://bugzilla.gnome.org/show_bug.cgi?id=662685
2012-01-25 21:14:40 +01:00
1d311e7916 shell-app: Make use of Keywords in search
.desktop files have been designed for browsing, so the existing
fields often produce insufficient results when used for search.
gnome-control-center used X-GNOME-Keywords for that purpose, which
has now been standardized as Keywords. It makes sense for us to
support it in gnome-shell as well (and encourage its use outside
of settings panels).

https://bugzilla.gnome.org/show_bug.cgi?id=609702
2012-01-25 20:29:37 +01:00
0c19f71c96 telepathyClient: Fix fallout from Lang.Class port
this.parent was ported from calling the parent class's method like
MessageTray.Notification.prototype._init.call(this, ...);. When
porting to Lang.Class, the 'this' parameter is now passed automatically, but
removing it was forgot in a few places. Fix these places.

https://bugzilla.gnome.org/show_bug.cgi?id=665017
2012-01-25 02:13:37 -05:00
8d6ab32b9a shell-app-system: Add Debian to the vendor prefixes
It seems that Debian has their own prefixes in something like
debian-xterm.desktop. To properly do application matching in these cases,
we need to strip the debian- prefix.

https://bugzilla.gnome.org/show_bug.cgi?id=665647
2012-01-25 02:11:11 -05:00
6c1a2d531f Updated Irish translation 2012-01-24 16:40:55 -07:00
c6e9f9742b dash: Use the correct theme node for spacing/padding
We consider spacing and padding in _adjustIconSize, but as we use
the theme node from an actor which is not exposed to the CSS, we
miss the "real" values - correct this.

https://bugzilla.gnome.org/show_bug.cgi?id=662213
2012-01-24 22:53:22 +01:00
d23aaf3cea Updated Finnish translation. 2012-01-24 15:37:21 +02:00
017fde91ad Updated Galician translations 2012-01-23 04:05:43 +01:00
31ffc5a85d theme: Fix some more fallout from background-size addition
The workspace-switcher-popup background should be at 100%, not upscaled
to contain the entire popup.

https://bugzilla.gnome.org/show_bug.cgi?id=668430
2012-01-22 06:09:01 -05:00
62b65a25d8 workspace: fix dragging of window thumbnails
Clear the ClutterClickAction state before starting the drag,
otherwise it will eat the first button event after the drag,
preventing a new drag from being started.

https://bugzilla.gnome.org/show_bug.cgi?id=662386
2012-01-22 06:06:35 -05:00
882fe48d80 Screenshot: Move filesystem I/O to a thread
Writting the screenshot to a file can take a relativly long time
in which we block the compositor, so do that part in a separate
thread to avoid the hang.

https://bugzilla.gnome.org/show_bug.cgi?id=652952
2012-01-22 11:47:56 +01:00
90a691ed25 Bump version to 3.3.4
Update NEWS
2012-01-20 21:12:39 -05:00
eadb41b3bb Bump dependencies on gtk+ 3.3.9 and glib 2.31.6 2012-01-20 20:46:01 +01:00
6829590c8f dash: improve timing of labels
DashItem labels have initial delay before showing up, but once the
first label in the dash is visible (meaning the user is very likely
exploring things) and the pointer is moved along the dash, the label
will follow immediately.

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

Signed-off-by: Seif Lotfy <seif.lotfy@collabora.co.uk>
2012-01-20 20:19:18 +01:00
29da720e6a notificationDaemon: fix order of arguments to _lookupSource()
The order of arguments passed to _lookupSource() was wrong, causing
problems when tray icons were removed.

https://bugzilla.gnome.org/show_bug.cgi?id=664138
2012-01-20 00:03:07 -05:00
c5932c0f07 Adjust to gtk/mutter changes for Application API
https://bugzilla.gnome.org/show_bug.cgi?id=668118
2012-01-18 17:25:35 -05:00
6195386a06 Update iconGrid label style to current mockup
Reference: http://git.gnome.org/browse/gnome-shell-design/plain/mockups/static/overview-application-picker.png

https://bugzilla.gnome.org/show_bug.cgi?id=642392
2012-01-18 19:17:46 +00:00
0080440118 workspaceThumbnail: fix window tracking bugs
- We should only call workspaceRemoved() for workspaces that are
  are actually being removed.
- When we have multiple monitors, a window on a secondary monitor is
  on all workspaces, so it ends up in all workspaces _allWindows
  lists, so we can't use previous presence in that list to determine
  whether we need to go ahead and add the actor; allWindows is simply
  the list of windows where we are listening  to notify::minimized.

https://bugzilla.gnome.org/show_bug.cgi?id=667652
2012-01-18 10:20:53 -05:00
b5be62cd1b ShellApp: Use unique name for menus
Otherwise since the auto-activate flag is set for dbus messages, we
may re-vivify applications that have DBus service files.

https://bugzilla.gnome.org/show_bug.cgi?id=667881
2012-01-17 14:00:51 -05:00
1bac40fbe3 App menu: only create the popup menu with a GMenu
Since the application proxy is created asynchronously, at the time
the GActionGroup (GActionMuxer) is created, there is no GDBusMenu yet.
Defer creating the menu in that case.
Also, clear out signal handlers if have no target application.

https://bugzilla.gnome.org/show_bug.cgi?id=633028
2012-01-17 18:40:04 +01:00
11637bae43 Network Menu: prefer VPN connections when showing the icon
When VPN is active, and it's not the default routing, we should
show the vpn icon, rather than the physical connection.

https://bugzilla.gnome.org/show_bug.cgi?id=665115
2012-01-17 18:24:44 +01:00
301bacec9f workspaceThumbnail: improve handling of notify::minimized signal
There were various cases where we could lose track of a window and
leave the notify::minimized signal connect after the actor was destroyed
or the workspace removed. This could result in operations on a removed
workspace and assertion failures inside Mutter.

https://bugzilla.redhat.com/show_bug.cgi?id=773059
https://bugzilla.gnome.org/show_bug.cgi?id=667652
2012-01-17 10:53:07 -05:00
8943b3b0e9 workspaceThumbnail: disconnect handlers when workspace is removed
We need to remove the handlers when the workspace is removed, not
when the animation of it being removed finishes, or we can access
a destroyed workspace and triggger an assertion failure in Mutter.

https://bugzilla.redhat.com/show_bug.cgi?id=705664
https://bugzilla.gnome.org/show_bug.cgi?id=667652
2012-01-17 10:53:07 -05:00
f59018f2d7 workspaceThumbnail: clear the drop placeholder on pointer leave
We add a drag monitor to check whether the pointer is inside
the workspace selector, and update the visibility of the drop
placeholder consequently.

https://bugzilla.gnome.org/show_bug.cgi?id=664201
2012-01-17 15:45:23 +01:00
235cb9c505 build: Add the 'gl' pkg-config
Cogl does not explicitly link against GL or GLES any more, and Clutter
master dropped the 'gl' pkg-config requirement because it introduced
unneeded and conflicting dependencies.

GNOME Shell still uses glXQuery* API, so it needs to explicitly link
against libGL.

https://bugzilla.gnome.org/show_bug.cgi?id=667864
2012-01-17 13:48:59 +00:00
3f328463a8 Fix timeout callback leaks
Make sure that we don't leak oneshot timeout handlers in main.js and
polkitAuthenticationAgent.js by making the callback return false.

https://bugzilla.gnome.org/show_bug.cgi?id=668087
2012-01-17 12:39:03 +01:00
e58c82fc04 theme-node-drawing: don't crash if st_theme_node_paint() is called on an empty area
When st_theme_node_paint() was called with zero width or height and a theme
node with a shadow, we'd crash because we'd fail to allocate a texture with
an empty size, then unreference the NULL pointer.

https://bugzilla.redhat.com/show_bug.cgi?id=748293
https://bugzilla.gnome.org/show_bug.cgi?id=668050
2012-01-16 17:38:38 -05:00
91ca86ffe4 st-theme-node-drawing: clear border buffer before drawing on it
A new texture has undefined contents - when we're creating a shadow,
we need to clear the contents of the texture before drawing the border
and background into it.

https://bugzilla.gnome.org/show_bug.cgi?id=668048
2012-01-16 17:38:38 -05:00
33d4518e50 hotplug-sniffer: fix double free when setting D-Bus return value
g_dbus_method_invocation_return_value() adopts a floating reference,
so we don't also need to unreference it; fix by replacing the code
using a more compact form using the ^ convenience character in
GVariant type specifications. (Thanks to Ryan Lortie for the
suggestion.)

https://bugzilla.gnome.org/show_bug.cgi?id=667378
2012-01-16 17:38:38 -05:00
b087191d2b RemoteMenu: add support for section labels
According to the GIO docs, sections can have labels too. We support
them by inserting a non reactive menu item at the beginning of the
section. This item is specially flagged to be ignored while processing
changed signals from the model (since it does not correspond to any
model item)

https://bugzilla.gnome.org/show_bug.cgi?id=666681
2012-01-16 19:29:50 +01:00
64baea1693 Workspace: Set a maximum scale for window clones
If there's a single small window (e.g. empathy chat) in the overview, it
looks usable, because it's as big as outside of the overview, but when
you start to type, overview search is launched, which is confusing.

Fix that by setting maximum scale for window clones to 0.7

https://bugzilla.gnome.org/show_bug.cgi?id=646704
2012-01-15 16:25:18 +01:00
b88b743428 Delay rearrangement when cursor hovers a window
In overview when closing a window and afterwards dragging a window it can
happen that you pick a wrong window or no window if windows' positions is
updated while initiating the drag.
Fix that by delaying window rearrangement when cursor is over a window.

https://bugzilla.gnome.org/show_bug.cgi?id=645325
2012-01-15 16:22:53 +01:00
c606cf076d Updated Slovenian translation 2012-01-15 13:46:05 +01:00
d205d7e7c2 Updated Galician translations 2012-01-15 02:34:44 +01:00
efdd3375d0 updated persian translation 2012-01-15 00:42:32 +03:30
abcca3d3bc ShellEmbeddedWindow: don't update the size of a destroyed actor.
shell_embedded_window_hide() can be called during widget destruction,
after the associated ClutterActor has been already cleared out.
Fix a crash in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=633028
2012-01-14 13:31:04 +01:00
f4d13b9801 ShellApp: don't use the app proxy before it's created
The application proxy is created asynchrously after the dbus name
is registed. This means that when tracking the first window (and
therefore creating the first window GActionGroup) there is no
app proxy yet.

https://bugzilla.gnome.org/show_bug.cgi?id=633028
2012-01-14 13:31:04 +01:00
7da39031e1 Place display: don't use a StIcon for DND
DND code assumes it can query the size of the actor before parenting,
while StWidget asserts that get_preferred_size() is only called
after the actor is on stage. This fixes a crash while dragging
"Connect to..."

https://bugzilla.gnome.org/show_bug.cgi?id=633028
2012-01-14 13:31:04 +01:00
6cdb1bd60c telepathyClient: handle ExtendedAway as Away and not Offline
https://bugzilla.gnome.org/show_bug.cgi?id=667813
2012-01-13 11:18:35 +01:00
7c108e267c dbus: fix Screenshot async methods fallout from GDBus migration
With GJS' GDBus implementation, we get the invocation paramters as an
array if we declare a method as async.
This is bad and not consistent with what GJS does for synchronous
methods, but it's the way it is, and other classes in gnome-shell
implement this correctly by exploding the array into its components in
the method implementation, but not the screenshot methods.
Also, we're supposed to return a value using the provided invocation
object, not with a callback now, so do that.

https://bugzilla.gnome.org/show_bug.cgi?id=667662
2012-01-12 15:44:38 -05:00
5cf06fe9a7 telepathyClient: No need to prepare channel contacts
tp-glib does it for us since version 0.15.6

https://bugzilla.gnome.org/show_bug.cgi?id=658817
2012-01-12 20:55:40 +01:00
41f6956197 Fix some fallout from background-size addition
Commit 25948f214e replaced the old hardcoded scaling behavior of
background-images with the CSS-compliant option to control that
behavior with the background-size property. Fix some fallout from
the changed default scaling behavior.
2012-01-10 21:50:21 +01:00
417cbea79c gsettings: Add migration file for overrides
Mutter/Metacity settings overridden by the shell have not been
migrated when moving to GSettings, but there's no good reason not
to migrate those preferences as well.

https://bugzilla.gnome.org/show_bug.cgi?id=667636
2012-01-10 19:40:01 +01:00
225c807550 Updated Finnish translation by Jiri Grönroos. 2012-01-10 09:28:22 +01:00
ff78d2655b Updated Belarusian translation. 2012-01-10 01:19:50 +03:00
bde15f7c61 Updated Bulgarian translation 2012-01-08 10:49:22 +02:00
30300f1aeb Updated Norwegian bokmål translation 2012-01-06 21:18:12 +01:00
d42c3a15d6 Updated Vietnamese translation 2012-01-06 16:36:00 +07:00
14a65559af po/vi: import from Damned Lies 2012-01-06 16:30:38 +07:00
ba1e7bd095 Updated Russian translation 2012-01-05 15:59:24 +04:00
951705a4b2 NEWS: small fixes for 3.3.3 release
Add a couple of missing translators and a missing bug ID from late changes.
2012-01-04 12:46:46 -05:00
36bf63a5de Bump version to 3.3.3
Update NEWS and require Mutter 3.3.3
2012-01-04 12:39:37 -05:00
66c4881fd2 Add missing files to distribution 2012-01-04 12:39:37 -05:00
c6b169cb33 docs: fix srcdir != builddir builds 2012-01-04 12:39:37 -05:00
0e753ed5a6 docs: Fix build to exclude private sources
Exclude private header files from the scanned set, and set the rpath
correctly to point to the bluetooth private library.
2012-01-04 11:57:51 -05:00
785969feb5 l10n: close bug #667204 2012-01-04 10:20:17 +01:00
56036476a4 Updated Japanese translation 2012-01-04 17:15:50 +09:00
936b1b5638 notificationDaemon: group sources based on a combination of pid and title
That way different system notifications, such as the ones about battery power
and the ones about software updates, are shown with separate message tray
sources.

https://bugzilla.gnome.org/show_bug.cgi?id=664138
2012-01-03 15:15:33 -05:00
bf3b94d654 Updated Danish translation 2012-01-03 12:14:47 +01:00
90b4a0856f [l10n]Updated Turkish translation 2012-01-02 09:34:21 +02:00
686190ce80 Updated Spanish translation 2011-12-31 19:04:09 +01:00
45495c2474 Updated Hebrew translation. 2011-12-30 10:50:54 +02:00
9caa88fecb Updated Norwegian Nynorsk translation 2011-12-26 14:31:34 +01:00
25948f214e St: Implement background-size CSS property
Implement the background-size CSS property, specified by the CSS
Backgrounds and Borders Module Level 3, including the keywords
"contain", "cover", and fixed-size backgrounds.

https://bugzilla.gnome.org/show_bug.cgi?id=633462
2011-12-23 14:23:23 -05:00
bea4faacd4 Updated Spanish translation 2011-12-23 14:26:02 +01:00
a7bd9f811b PopupMenu: disconnect from 'destroy' signals of destroyed items
After an item is destroyed, all its signals were disconnected,
except for 'destroy' itself. This could lead to exceptions, if
destroy was called more than once on the item.

https://bugzilla.gnome.org/show_bug.cgi?id=665680
2011-12-22 18:00:52 +01:00
2b9561fcbb dash: Move labels beside the app icon upon hovering
Instead of using an St.Tooltip to show the app's name under the icon,
manually position a new St.Label ourselves. Make sure to keep the label
hidden when right-clicking so it doesn't get in the way of the popup menu.
Only one tooltip/label will be displayed at a time.

https://bugzilla.gnome.org/show_bug.cgi?id=666166
2011-12-22 09:07:48 +00:00
7b9c9b2f7d shell-app: ensure there's always a muxer when setting actions on it
It's not guaranteed that the application DBus proxy appears before
we receive the first focus event from the toplevel window.
Ensure that the first method to access the action muxer creates it if
hasn't been created yet.
2011-12-21 16:36:56 +01:00
b47b82ed42 build: Split out "private" sources variable that can be easily appended
Move the action muxing copy stuff in there.
2011-12-20 17:36:59 -05:00
c4c2c11dca action muxer: drop direct GDBusActionGroup use
We now have GRemoteActionGroup interface with the needed API,
implemented by GDBusActionGroup.

With the new API we could theoretically turn GActionMuxer itself into a
GRemoteActionGroup and expose the _full API to the shell so that the
timestamps could be passed from there.
2011-12-20 17:36:59 -05:00
ce38293a0f GActionMuxer: pass platform data
Pass the current timestamp as platform data when activating
an action. This is implemented slightly hacky, since we use
clutter_get_current_event_time() to get at the timestamp, but
the alternative is to expose g_action_muxer_activate_action_full
to js, which would be quite a bit more involved.
2011-12-20 17:36:59 -05:00
6c4e9d23f2 Add per-window actions
GTK+ also exports window-specific actions, by putting the object path
for the exported action group in the _DBUS_OBJECT_PATH X property.
We add this action group to the app's muxer with a 'win' prefix,
since that is what the exported menu expects. Whenever the focus
window changes, we update the window-specific actions of its
application, and emit notify::action-group to cause the app
menu to be updated.
2011-12-20 17:36:59 -05:00
4aa1fe9ca2 Another update for GLib API changes
GDBusActionGroup api has changed again, adapt to that.
Also, use a GActionMuxer to add the 'app.' prefix to actions,
instead of manually stripping it out of the action names.
In the future, the muxer will also contain per-window actions
with a 'win.' prefix.
2011-12-20 17:36:59 -05:00
951fff5aa0 Application Menu: watch for menu property changes
By the time the window is first mapped and the app menu button is
synced, we may not have finished reading the menu. In that case,
connect to notify::menu and update accordingly.
2011-12-20 17:36:59 -05:00
5ad8080cb9 Application Menu: update for latest GMenu changes
GMenuProxy has been replaced by GDBusMenuModel, and the object path
has been moved (now needs to be retrieved from the AppMenu GApplication
property).
Update the test to prefix each action with "app." as documented,
and use a GtkApplicationWindow instead of a plain GtkWindow.
2011-12-20 17:36:59 -05:00
e53e3cbb09 test-gapplication: update for latest gapplication changes
g_application_set_action_group is deprecated, we should use
GActionMap. Also, GSimpleActions can now be constructed as normal
GObjects.
2011-12-20 17:36:59 -05:00
8a029f333f popupMenu: Remove app. from app actions 2011-12-20 17:36:59 -05:00
4debedb275 Application Menu: add support for showing GApplication actions
Use the new GApplication support in ShellApp to create the application
menu. Supports plain (no state), boolean and double actions.
Includes a test application (as no other application uses GApplication
for actions)

https://bugzilla.gnome.org/show_bug.cgi?id=621203
2011-12-20 17:36:59 -05:00
8764253861 ShellApp: port to new GDBusActionGroup and GMenuProxy API
GDBusActionGroup and GMenuProxy are new objects in GIO 2.32 that
help with accessing menus and actions of remote applications.
This patch makes it possible for the shell to associate an
application with a dbus name and from that a GMenu, that will
be shown as the application menu.

https://bugzilla.gnome.org/show_bug.cgi?id=621203
2011-12-20 17:36:59 -05:00
bbdce159fa Util: fix binary search exit condition
The loop can exit with an interval of length one or one of
length zero. In the first case it is correct to check which side
of the interval to return, in the second case no comparison should
be made (since there is only one possible value).
In practice, this usually results in one comparison more than needed,
but in some cases (when the position was past the end of the array),
would call the comparator with undefined.

https://bugzilla.gnome.org/show_bug.cgi?id=666614
2011-12-20 22:44:03 +01:00
087d8e602e Updated POTFILES.in 2011-12-20 20:39:12 +01:00
50aa15dec9 Reintroduce Wanda The Fish
When transitioning from gnome-panel to gnome-shell in 3.0 we
lost the ability to summon the wisdom of the mythical fish.
This patch restores this, for the few adepts that are aware of
the magical incantation.
(Not as configurable as the original one, but it's an easter egg
after all...)

https://bugzilla.gnome.org/show_bug.cgi?id=666606
2011-12-20 20:06:14 +01:00
26580f8f2c IconGrid: don't force the size of the icon
Forcing the icon size will distort it unnecessarily, and will
in any case not work if showing an animation (which is a ClutterGroup).
Instead, set the size on the bin, and make it align its child
if needed.

https://bugzilla.gnome.org/show_bug.cgi?id=666606
2011-12-20 19:25:52 +01:00
875b6d131b StTextureCache: fix load_sliced_image with different sizes
grid_width and grid_height were inverted, which caused a crash
in GdkPixbuf code. This was never noticed because the animation
in the panel is a square.

https://bugzilla.gnome.org/show_bug.cgi?id=666606
2011-12-20 19:25:43 +01:00
77afd6782f NetworkMenu: actually add new access point to the network list
Previously the code in _accessPointAdded was iterating over the
the network list to find a good place, and at that time, added both
the network to the list and the item to the menu. When I refactored
to call queueCreateSection, I forgot to add code to insert the
network in the list.
Add it now, using the new Util.insertSorted function.

https://bugzilla.gnome.org/show_bug.cgi?id=666429
2011-12-19 16:54:46 +01:00
09ab13cf04 Array: add capability to insert while keeping a specific order
Adds two new functions, Util.lowerBound and Util.insertSorted,
that take an array, a value and a comparator, and find the
first position at which the value can be inserted without
violating the order, in optimal time.

https://bugzilla.gnome.org/show_bug.cgi?id=666429
2011-12-19 16:54:46 +01:00
56f312dc03 st: Fix typo
Commit c7846e1 introduced a small typo, fix.
2011-12-19 16:08:37 +01:00
f2cbddc196 st-theme-node: Fix memory leak
We ref the icon_colors in st_theme_node_get_icon_colors, but never
unref it.
2011-12-19 09:47:26 -05:00
c7846e172f st-icon: Fix memory leak
We were never freeing the dup'd icon_name string for an StIcon.
2011-12-19 09:47:26 -05:00
54afb7b25e browser-plugin: Fix installation on WebKit-like browsers
I missed this use of raw UTF8Characters last time. Hopefully this
should be the last fix required for WebKit support.

https://bugzilla.gnome.org/show_bug.cgi?id=666444
2011-12-18 18:29:55 -05:00
f3cb9d0443 Network Menu: update the UI only when really needed
By using Main.queueDeferredWork, we can ensure that most of the
menu contents (in particular, the heaviest parts like the list of
wifi networks) are not updated immediately as we receive signals
from NetworkManager. Instead, the menu is rebuilt some time later,
or as soon as the user opens the menu.
This means that it is no longer needed to optimize for the
access-point-added case, replacing a lot of buggy code with a safer
call to _queueCreateSection, which in turn should ensure that the
more menu, if existing, is always at the end and that at most 5 networks
are visible outside it.

https://bugzilla.gnome.org/show_bug.cgi?id=664124
2011-12-16 08:58:11 +01:00
fcee7f2f3a run-js-test: Do not use the default stage
This was left out in commit faff0738eb.
2011-12-16 00:18:17 +01:00
faff0738eb Do not use the default stage
https://bugzilla.gnome.org/show_bug.cgi?id=664052
2011-12-15 16:13:29 -05:00
69e26c6dee telepathyClient: only notify on new channels when asked to do so
We need to notify when the channel dispatcher calls HandleChannels on
us with a channel we already handle. However, we don't want to notify
if we claim a new incoming channel which doesn't actually have
anything interesting in it yet.

For example, a new channel pops up just to give a delivery
notification. We want (or, need) to handle it but don't want to notify
for it.

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

Signed-off-by: Jonny Lamb <jonnylamb@gnome.org>
2011-12-15 16:04:45 +00:00
1acec65c5e theme - update window captions to match mockups
Change window caption text weight, padding and radius.

https://bugzilla.gnome.org/show_bug.cgi?id=664487
2011-12-14 11:46:24 +00:00
8a6a3968c3 workspace: Remove tweens when hiding overlays
The window overlays may be shown erroneously if hideOverlays is
called while the corresponding clone has an uncompleted tween which
calls showOverlays in its onComplete handler, for instance when
quickly leaving the overview before the initial overview animation
has finished. To fix, remove all existing tweens when hiding the
overlays.

https://bugzilla.gnome.org/show_bug.cgi?id=666020
2011-12-12 20:02:51 +01:00
58e4870cb8 Uploaded Ukranian 2011-12-11 16:17:41 +02:00
102099cadd Updated Hebrew translation. 2011-12-10 10:40:25 +02:00
efe6d06ddd telepathyClient: reset badge as soon as the summary notification is shown
That way the user will not see an outdated pending messages count while
they are viewing them or chatting with the sender.

https://bugzilla.gnome.org/show_bug.cgi?id=659272
2011-12-09 11:48:38 -05:00
f0d0e025dd Added Macedonian translation. 2011-12-08 22:49:25 +01:00
fc2d0215f5 Added Macedonian translation. 2011-12-08 22:40:52 +01:00
02af8eb824 browser-plugin: Fix memory leak when passing an invalid UUID
https://bugzilla.gnome.org/show_bug.cgi?id=665261
2011-12-08 11:50:35 -05:00
102f2a91f9 Make the window labels visible at all times
When in overview, window labels flicker or are temporarily hidden on a
number of occasions - when simply clicking around the area the windows
are displayed in, dragging a window, sliding in the workspace list,
adding new workspaces etc. This patch makes the label for any window
visible at any given moment when in overview and the said window is
not being dragged around.

https://bugzilla.gnome.org/show_bug.cgi?id=644861
2011-12-08 14:15:13 +00:00
2c649d5da5 Update Czech translation 2011-12-07 17:11:32 +01:00
7fa7d04ed0 NetworkMenu: show hardware disabled when rfkill is active
When wifi or wwan are blocked by hardware killswitch, we should not
allow changing the switch (it won't work anyway), and show
"hardware disabled" instead, similar to what we already do in the
bluetooth menu.

https://bugzilla.gnome.org/show_bug.cgi?id=665194
2011-12-07 15:36:18 +01:00
aad9179373 NetworkMenu: fix number of visible networks
When placing networks in _createSection, we were taking in
consideration that _activeNetwork is always first, by adding 1,
but then kept this offset also for networks following it (normally,
all of them, since _activeNetwork is also the most recently used),
that instead should not be affected by the movement.
This resulted in the menu showing 4 networks + More... instead of
5.

https://bugzilla.gnome.org/show_bug.cgi?id=664124
2011-12-07 15:35:19 +01:00
d5b4e30eb7 browser-plugin: Fix crash if shell is not running 2011-12-06 17:30:35 +01:00
34ba6f2f71 Updated Lithuanian translation 2011-12-06 17:07:46 +02:00
d845f40b98 Updated Lithuanian translation 2011-12-06 17:00:49 +02:00
80c16aa8f7 layout: Make ripple boxes initially invisible
The three boxes for the ripple animation are visible when created. This
means that the drag and drop code that searches for an actor to handle
the drag can find the ripple boxes instead of the Activities button or
hot corner. The latter can handle drag and drop while the ripple boxes
can't.
This is only a problem if drag and drop is attempted before the ripple
animation has been played: the boxes are made invisible at the end of
the animation. The fix is to just create the boxes invisible.
2011-12-05 10:39:14 +01:00
0cbaeaefed messageTray: use a "hot corner" to summon the tray
Instead of leaving the tray covering the whole last pixel row when it's
hidden, hide it completely. This avoids mouse events not being delivered to
application windows on the last pixel row.

To summon the tray we use a single reactive pixel on the corner.

https://bugzilla.gnome.org/show_bug.cgi?id=663366
2011-12-05 04:51:16 +00:00
ab6a7773ce browser-plugin: Make sure to use the UTF8Length parameter
Some plugin hosts may have junk after the UTF8Characters that we need to strip
off. No current browsers that I know of do this, but it still helps to be
correct.
2011-12-04 10:31:04 -05:00
85e243982b autorun-manager: Use app names rather than full names
https://bugzilla.gnome.org/show_bug.cgi?id=665461
2011-12-04 15:38:03 +01:00
6eb168bc68 Updated Finnish translation 2011-12-04 00:16:01 +02:00
47b55e29d4 workspace-thumbnails: Emit 'window-drag-cancelled'
The dash handles 'window-drag-cancelled', to be able to do the
animations for drag snap-back and size changes in parallel. As
the signal is not emitted for previews in the workspace switcher,
it does not work in that case.
2011-12-02 17:08:39 +01:00
2c243b678f Updated Norwegian bokmål translation 2011-12-01 20:18:28 +01:00
a634f25d30 workspaces-view: Handle swipe scolling in workspacesDisplay
If workspaces-only-on-primary is false, swipe scrolling is now
broken with multiple monitors. To fix, let workspacesDisplay
handle swipe scrolling for all views.

https://bugzilla.gnome.org/show_bug.cgi?id=652580
2011-12-01 17:55:13 +01:00
26df6cf35c workspaces-display: Implement workspaces on non-primary monitors
If workspaces-only-on-primary is false, workspaces should be shown
on each monitor; rather than letting the existing workspaces span
the entire screen, manage one workspacesView per monitor (similar
to the extra workspaces in WorkspacesView when the setting is true).

https://bugzilla.gnome.org/show_bug.cgi?id=652580
2011-12-01 17:55:13 +01:00
84dde8e9bb workspaces-view: Don't create extra workspaces unconditionally
Extra workspaces are special, in that they collect windows from
all workspaces for a particular monitor. This matches the default
behavior, but we need more than a single workspace per monitor if
workspaces-only-on-primary is false, so don't create the extra
workspaces in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=652580
2011-12-01 17:55:13 +01:00
f2c79be11a workspaces-display: Make workspacesView private
WorkspacesDisplay was introduced to manage the workspace objects
and views; however, the overview still accesses the view held
by the workspacesDisplay directly, which is a bit odd.
Add some additional methods needed by the overview, and make the
view a private property.

https://bugzilla.gnome.org/show_bug.cgi?id=652580
2011-12-01 17:55:13 +01:00
10df80b96a status: Remove unneeded pkill call
The bluetooth-applet is only started in the fallback session,
not when running under the shell.

https://bugzilla.gnome.org/show_bug.cgi?id=647587
2011-12-01 10:51:19 +01:00
bbb83656bf extensionSystem: Set the proper 'enabled' and 'type' parameters
When installing an extension at runtime, we accidentally swapped the 'type'
and 'enabled' parameters. While this doesn't directly affect anything right
now, as everything works coincidentally, future patches that look at the
'type' parameter to decide what to do would get the wrong answer.
2011-11-30 22:04:00 -05:00
ef49670ae4 fileUtils: Fix recursivelyDeleteDir 2011-11-30 22:04:00 -05:00
e4df00c77d Updated Telugu Translation 2011-11-29 23:12:12 +05:30
3d70662716 Updated Romanian translation 2011-11-27 05:23:35 +02:00
8d0638a187 Update Simplified Chinese translation. 2011-11-26 09:39:55 +00:00
06143396b7 Updated Slovenian translation 2011-11-25 21:24:36 +01:00
17c46c2452 Port everything to class framework
The last patch in the sequence. Every place that was previously
setting prototype has been ported to Lang.Class, to make code more
concise and allow for better toString().

https://bugzilla.gnome.org/show_bug.cgi?id=664436
2011-11-24 09:50:04 +01:00
0996174b3d Port GDM and Caribou to GDBus
During the mass port to GDBus, this classes were left out (probably
because they didn't exist at the time). Now it's time to update
them.

https://bugzilla.gnome.org/show_bug.cgi?id=664436
2011-11-24 09:50:04 +01:00
d6b6f814d3 Port all classes with inheritance to class framework
All classes that have at least one other derived class (and thus
benefit from the framework) have been now ported. These includes
NMDevice, SearchProvider, AltTab.SwitcherList, and some other
stuff around.

https://bugzilla.gnome.org/show_bug.cgi?id=664436
2011-11-24 09:50:04 +01:00
987099ea55 Port ModalDialog to the class framework
Similar to the previous commits, time to port shell modal dialogs
to the class framework.

https://bugzilla.gnome.org/show_bug.cgi?id=664436
2011-11-24 09:50:04 +01:00
b356aa8e3b Port message tray sources and notifications to class framework
Third step in the class framework port, now it's the turn of
MessageTray.Source and MessageTray.Notification, as well as
the various implementations around the shell.

https://bugzilla.gnome.org/show_bug.cgi?id=664436
2011-11-24 09:50:04 +01:00
566bdb50c2 Port PanelMenu to new class framework
Second patch in the class framework, now it's the turn of
PanelMenu (buttons, menus and status indicators).

https://bugzilla.gnome.org/show_bug.cgi?id=664436
2011-11-24 09:50:04 +01:00
2b57603271 Port PopupMenu to new Lang.Class framework
The Lang module in gjs has recently gained a small yet powerful
Class framework, that should help improve the readability of code
when using complex inheritance.
This commit starts porting shell code, by rewriting all classes in
popupMenu.js (and all derived classes) to Lang.Class.

https://bugzilla.gnome.org/show_bug.cgi?id=664436
2011-11-24 09:50:04 +01:00
c3528f5b6b lookingGlass: Fix global key press handler
No idea why connecting a key-press-event to a non-reactive actor
used to work, but some Clutter update broke it. Obvious fix is
to make the actor reactive.

https://bugzilla.gnome.org/show_bug.cgi?id=664582
2011-11-22 22:33:05 +01:00
4c7cc94cdc build: Add test/unit/jsParse.js to Makefile
Commit 3941961f8b added the file without referencing it in the Makefile,
which breaks distcheck.
2011-11-22 15:34:22 +01:00
5ff2285707 Bump version to 3.3.2
- Require Mutter 3.3.2 for keybindings additions
 - Update NEWS
2011-11-21 19:38:55 -05:00
d714dfd82e shell-wm: Remove takeover_keybinding()
Introspection support is now good enough to set a custom keybinding
handler directly from JS.

https://bugzilla.gnome.org/show_bug.cgi?id=663584
2011-11-22 00:42:28 +01:00
b1064cbe50 WorkspaceThumnail: fix typo
An if is missing, causing the subsequent expression to evaluate to
nothing, and the invocation it should be protect to be unconditional.
2011-11-21 21:59:09 +01:00
8d3e5ea507 Make notification icon buttons elegant.
The huge icons had little whitespace and don't scale pixel precise.
2011-11-21 15:39:36 +01:00
001b6afc7e Updated Greek translation 2011-11-21 09:14:13 +02:00
55d6c5ea8f polkit: Find the best user to authenticate as
We prefer to ask the user for his own password. If PolicyKit
is not configured to accept that, try the root password. If
PolicyKit does not accept that either, ask for password of
the first user that PolicyKit _will_ accept. The last case
is a bit broken, but should rarely occur in real-life
configurations.

https://bugzilla.gnome.org/show_bug.cgi?id=651547
2011-11-18 17:28:58 -05:00
fa2ff8158f Updated Spanish translation 2011-11-16 14:23:30 +01:00
97e7ea0b5d shellDBus: Ignore extension properties that we don't care about 2011-11-15 16:48:20 -05:00
65dec2b72a shellDBus: Add missing initialization 2011-11-15 16:48:20 -05:00
5fd3ca8d09 Updated Finnish translation 2011-11-15 22:21:46 +02:00
dc9a8d505d Drop a leftover mention of GConf from configure
There is no GConf usage anymore, so drop it for good.
2011-11-15 09:24:48 -05:00
fc8d13f4bd GDBus: restore non-fatality of name acquisition error
commit 5350302b09 dropped the possibility
to make a dbus name acquisition failure non-fatal.
Btw, it has also overriden the name in the error message.

Signed-off-by: Marc-Antoine Perennou <Marc-Antoine@Perennou.com>
Signed-off-by: Colin Walters <walters@verbum.org>

https://bugzilla.gnome.org/show_bug.cgi?id=663941
2011-11-15 09:19:19 -05:00
d1aa7de5d0 Drop last remnants of dbus-glib from configure
GDMUSER has been unused since before 3.2.
2011-11-15 09:17:50 -05:00
e279ef1c7c user-menu: Disable combo box if no accounts are enabled
If no telepathy accounts have been set up or enabled, the IM status
chooser won't have any effect. To avoid confusing behavior, make
the status selector insensitive in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=662800
2011-11-15 13:54:29 +01:00
b88657ab83 combo-box-menu-item: Propagate pseudo classes to the combo menu
ComboBoxMenuItems use ClutterClones to reconstruct the active item
in the associated ComboMenu, so pseudo class changes due to state
changes of the ComboBoxMenuItem don't have the intended effect
(since the actual style information is taken from the associated
ComboBoxMenu item).
As a fix, propagate relevant pseudo class changes to the active
ComboBoxMenu item.

https://bugzilla.gnome.org/show_bug.cgi?id=662799
2011-11-15 13:54:29 +01:00
d20e646ed6 combo-box-menu-item: Propagate style changes to the combo menu
ComboBoxMenuItems use ClutterClones to reconstruct the active item
in the associated ComboMenu to not impose a particular MenuItem type
in the menu. However, this results in style changes (for instance
those triggered by icon-theme or text-scaling-factor changes) of
the ComboBoxMenuItem not having a visual effect until the ComboBoxMenu
is shown.
As a fix, force a style update on the ComboBoxMenu when the item's
style changes.

https://bugzilla.gnome.org/show_bug.cgi?id=662799
2011-11-15 13:46:08 +01:00
8678b87120 a11y: Adapt to re-addition of 'visual-bell'
The option was merge with 'visual-bell-type' with the GSettings
port, but the change turned out too disruptive for the universal
access menu / settings panel, so gsettings-desktop-schemas commit
a5819b2a4e9 re-added the separate option.
2011-11-14 15:45:51 +01:00
960571a589 Updated Galician translations 2011-11-13 22:27:56 +01:00
d856338f86 bluetooth: Fix undefined variable issue
https://bugzilla.gnome.org/show_bug.cgi?id=663891
2011-11-13 15:23:38 -05:00
2b6b2d93a9 shellDBus: Fix missing user of old DBus API
https://bugzilla.gnome.org/show_bug.cgi?id=663902
2011-11-12 17:54:47 -05:00
363bd04166 browser-plugin: Move "entry points" comment
https://bugzilla.gnome.org/show_bug.cgi?id=663823
2011-11-12 13:38:36 -05:00
9bc1a68fe4 browser-plugin: Use g_strndup to get a string property
WebKit-based browsers like Chromium and Epiphany may insert extra junk at the
end of NPStrings, so we cannot depend on the strlen matching.

https://bugzilla.gnome.org/show_bug.cgi?id=663823
2011-11-12 13:38:36 -05:00
2c2729f7be browser-plugin: Set that we need XEmbed
This makes the plugin work under WebKit-based browsers such as Chromium and
Epiphany. See http://code.google.com/p/chromium/issues/detail?id=38229 and
WindowedCreatePlugin() in
http://src.chromium.org/viewvc/chrome/trunk/src/webkit/plugins/npapi/webplugin_delegate_impl_gtk.cc?revision=86823&content-type=text%2Fplain
for more information.

https://bugzilla.gnome.org/show_bug.cgi?id=663823
2011-11-12 13:38:36 -05:00
9011959356 Updated Traditional Chinese translation(Hong Kong and Taiwan) 2011-11-12 19:05:01 +08:00
c79b8bbe7e [l10n] Updated Estonian translation 2011-11-12 12:47:58 +02:00
da59eebf8e Updated British English translation 2011-11-12 07:49:25 +00:00
7854024326 user-menu: Fix fallout from GDBus port
- replace some left-over references to GnomeSession.Presence.setStatus()
 - the correct replacement for GnomeSession.Presence.getStatus()
   is *not* GnomeSession.Presence.connectSignal('StatusChanged')
2011-11-12 00:00:15 +01:00
a9ab8784c4 Adapt to mutter moving to GSettings
https://bugzilla.gnome.org/show_bug.cgi?id=663429
2011-11-11 20:32:43 +01:00
de5b00fd52 Updated Norwegian bokmål translation 2011-11-11 18:58:33 +01:00
6547f75b12 Port client side code to GDBus
This continues the series of patches for GDBus porting, affecting
all code that accesses remote DBus objects. This includes modemManager,
automount, autorun (for the hotplug sniffer), calendar, network (for
nm-applet only), power, scripting (for perf monitor interface)

https://bugzilla.gnome.org/show_bug.cgi?id=648651
2011-11-11 11:15:38 -05:00
827bf506a7 notificationDaemon, magnifierDBus: port to GDBus
Move /org/freedesktop/Notifications and /org/gnome/Magnifier to the
GDBus connection, so they're matched with the appropriate DBus name.

https://bugzilla.gnome.org/show_bug.cgi?id=648651
2011-11-11 11:15:38 -05:00
adc187c32e screensaver, gnomesession: port to GDBus based bindings
Port org.gnome.ScreenSaver and org.gnome.SessionManager glue code
to use GDBus, and move /org/gnome/Shell/EndSessionDialog to the
GDBus connection, so it is backed by the org.gnome.Shell name.

https://bugzilla.gnome.org/show_bug.cgi?id=648651
2011-11-11 11:15:38 -05:00
5350302b09 Port to new GDBus bindings in gjs
Rewrite code acquiring dbus names so that it uses GDBus, and rewrite
ShellDBus so that it is exposed on the GDBus connection. Ports of
the other objects will follow.

https://bugzilla.gnome.org/show_bug.cgi?id=648651
2011-11-11 11:15:38 -05:00
167ca75388 Replace deprecated gtk_widget_size_request 2011-11-11 09:50:18 +01:00
46cea67258 app: Fix crash on search
Not all desktop files tracked by the shell have
Exec lines.  This could be because they're actually
run by another process, for instance, and the desktop
file is merely there to provide metadata.  For example,
nautilus-pastebin provides a desktop file without an
Exec line.

The shell currently crashes if one of these partial
desktop files is installed and the user attempts to
search from the overview.

commit 37726a4cb6 fixed
a similar crasher.

This commit fixes the next one lower in the code.

https://bugzilla.gnome.org/show_bug.cgi?id=663815
2011-11-10 17:45:18 -05:00
463e0919d4 Remove all stray imports to imports.dbus
Some modules were importing DBus without actually using it.

https://bugzilla.gnome.org/show_bug.cgi?id=648651
2011-11-09 13:56:42 -05:00
98906c1da3 Updated Japanese translation 2011-11-09 21:24:15 +09:00
fry
b71e66c335 window-manager: Fix variable name
In _shouldAnimate() _animationBlockCount was referred to as
_animationsBlocked, fix this.

https://bugzilla.gnome.org/show_bug.cgi?id=662394
2011-11-08 01:57:20 +01:00
2b6c5bb416 main: Fix shell_dbus_acquire_names()
Commit 39727d1156 refactored dbus acquisition, but due to wrong use
of va_args we would only ever acquire the first bus name passed.

https://bugzilla.gnome.org/show_bug.cgi?id=658078
2011-11-08 01:01:10 +01:00
628e59894b Doc fixes
https://bugzilla.gnome.org/show_bug.cgi?id=663277
2011-11-07 15:24:59 -05:00
703d2ead33 workspaceThumbnail: Allow users to create workspaces at any position
Allow a user to create a new workspace by dragging a window or a launcher in
the middle of two existing ones.

https://bugzilla.gnome.org/show_bug.cgi?id=646409
2011-11-07 14:36:06 -05:00
43f53a708f NetworkMenu: fix regression in access-point-removed
When changing _findNetwork with _findExistingNetwork, I changed
the return value to avoid searching twice for the access point,
and changed some names. I forgot to update all points where those
names were used.

https://bugzilla.gnome.org/show_bug.cgi?id=663278
2011-11-06 16:55:30 -05:00
07e7331e7b gdm: Add a translator comment for 'Not Listed?'
https://bugzilla.gnome.org/show_bug.cgi?id=659946
2011-11-06 11:38:04 -05:00
c516af3130 Updated Spanish translation 2011-11-06 13:35:00 +01:00
39727d1156 main: factor out dbus names acquisition
This way it will be a lot shorter to add
a new name acquisition in the future

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

Signed-off-by: Marc-Antoine Perennou <Marc-Antoine@Perennou.com>
2011-11-06 05:43:37 -05:00
85cd189a69 shellDBus: Lock down Eval() to be a development tool only
https://bugzilla.gnome.org/show_bug.cgi?id=662891
2011-11-05 17:00:53 -04:00
a8e35422f2 [l10n] Updated German translation 2011-11-05 21:11:04 +01:00
3941961f8b lookingGlass: Add tab-completion
https://bugzilla.gnome.org/show_bug.cgi?id=661054
2011-11-05 13:05:11 -04:00
d9c6485cbf data: Fix the description for enabled-extensions
The description for enabled-extensions referenced an old and removed key,
"disabled-extensions". Update the description to talk about the DBus methods
that GNOME Shell provides and talk about how the list is now an explicit
whitelist that needs to be there.

https://bugzilla.gnome.org/show_bug.cgi?id=663175
2011-11-05 09:22:49 -04:00
c52ccc76a3 build: Fix dependencies for debian-based distros
https://bugzilla.gnome.org/show_bug.cgi?id=659770
2011-11-05 13:52:36 +01:00
be1c4f26b5 magnifier: Use enum from gsettings-desktop-schemas
gsettings-desktop-schemas installs a public header file for enum
types in schemas - use those instead of mirroring the types in JS.

https://bugzilla.gnome.org/show_bug.cgi?id=662238
2011-11-04 23:28:53 +01:00
a6ee6739e0 ctrlAltTab: fix popup's allocation when primary.x != 0
As in commit 3944df1bd2 but for ctrlAltTab's
popup.

https://bugzilla.gnome.org/show_bug.cgi?id=662502
2011-11-04 18:45:16 +00:00
54a8ad8bc6 Updated Spanish translation 2011-11-04 13:42:20 +01:00
2541bfcdf2 Updated Galician translations 2011-11-04 12:57:07 +01:00
d7d5da0301 NetworkMenu: fix logic for updating wifi icon
Previously, we connected to notify::strength only if there was
already a signal connected, and the AP changed (thus, by induction,
we never connected). As a result, the icon became stale and different
from that shown inside the menu (which is correctly updated).

https://bugzilla.gnome.org/show_bug.cgi?id=650007
2011-11-04 10:19:20 +01:00
d2bd9efc25 keyboard: fix exception: global.current_event_time is not a function 2011-11-04 01:40:30 +00:00
779b18bf48 messageTray: don't steal focus when popping under the pointer
We must look for the actor under the pointer in the whole message tray and not
just in the notification. This will avoid us to capture focus when a
notification comes up with the pointer on the whole tray area.

https://bugzilla.gnome.org/show_bug.cgi?id=661358
2011-11-04 01:40:30 +00:00
618a53b34f build: Fix when bluetooth is disabled 2011-11-03 21:27:50 -04:00
f4eaadb948 Pass bluetooth directory to g-ir-scanner
Rather than relying on the .la file that jhbuild deletes, we
explicitly tell g-ir-scanner to look in this subdirectory.
2011-11-03 18:26:03 -04:00
ea061b0f46 configure: Turn off -Werror by default
We will eventually land jhbuild work to grep for warnings; for now
breaking the build is just too painful.
2011-11-03 14:58:10 -04:00
3652e42699 messageTray: Add option to (un)mute conversations
Add "Mute"/"Unmute" option to the right click menu for chats to allow muting conversations
without blocking the sender or disabling all non-urgent notifications. Muting a conversation
prevents the pop up of notifications on new messages from the muted source, while these
messages are still available from the summary notification in the message tray.

https://bugzilla.gnome.org/show_bug.cgi?id=659962
2011-11-03 12:18:34 -04:00
398489f661 popup-menu: Add minimal handling of open/close to Sections
SubMenuMenuItems close automatically with their parent, however
closing fails when the parent item is a MenuSection, as those
currently ignore any open()/close() requests.
At some minimal handling by emitting the 'open-state-changed' signal,
so children like SubMenuMenuItems work as expected.

https://bugzilla.gnome.org/show_bug.cgi?id=661029
2011-11-03 15:36:57 +01:00
70fc13500d Network Menu: fix pulling out the first element from the More... submenu.
PopupMenu.firstMenuItem returns a PopupMenuItem, not an apObj. We
need to retrive the latter using the _apObj property.

Also, somehow the property from the number of elements in a menu
was changed from .length to .numMenuItems, and this broke the
destruction of the menu upon emptying it.

https://bugzilla.gnome.org/show_bug.cgi?id=659277
2011-11-03 13:12:03 +01:00
18541c447e messageTray: Reduce the scroll view fade
https://bugzilla.gnome.org/show_bug.cgi?id=662226
2011-10-31 13:45:57 -04:00
3294a6e1a7 theme: Lighten up the sent message color, clean up
Properly apply "received" style and drop unused border-radius styles
now that the messages have no background color

https://bugzilla.gnome.org/show_bug.cgi?id=658096
2011-10-31 12:41:48 -04:00
38563c38e8 Updated Czech translation 2011-10-30 14:33:55 +01:00
a465c5f996 theme: Add a selected-color to the polkit and network auth dialogs
https://bugzilla.gnome.org/show_bug.cgi?id=662969
2011-10-29 19:16:02 -04:00
1760ba1279 st-texture-cache: Fix colored symbolic icons
Commit b7bf712b97 broke colored symbolic icons by never including
the requested colors in the texture load request.

https://bugzilla.gnome.org/show_bug.cgi?id=662998
2011-10-29 19:33:30 +02:00
3d3c9546a2 NetworkMenu: don't query DBus properties of removed objects
Calling nm_access_point_get_ssid() in the handler of the
access-point-removed signal can result in DBus request, which will
then fail because the object was already removed at the server side.
Instead, use a difference function to retrieve the access point
object (the network), that compares directly by object identity.

https://bugzilla.gnome.org/show_bug.cgi?id=651378
2011-10-28 22:18:21 +02:00
203c5db5eb extensionSystem: Remove duplciated version check
This piece of accidentally duplicated code made sure that the OUT_OF_DATE
status was never set.

https://bugzilla.gnome.org/show_bug.cgi?id=662967
2011-10-28 16:12:52 -04:00
cf44234323 extensionSystem: Fix rebasing of extensions
We need to remove the extension from the order after it's disabled

https://bugzilla.gnome.org/show_bug.cgi?id=662704
2011-10-27 17:21:54 -04:00
cc94076ffb extensionSystem: Fix deferred loading of extensions
We need to show the list of installed extensions on EGO, so we can't defer
loading by not creating the extension meta.

https://bugzilla.gnome.org/show_bug.cgi?id=662704
2011-10-27 17:21:49 -04:00
8d137eae5b Updated Esperanto translation 2011-10-26 19:08:47 +02:00
51fa9ae513 Updated Swedish translation 2011-10-26 11:55:58 +02:00
9951c92459 Updated Slovenian translation 2011-10-25 21:17:43 +02:00
ee77e5d582 Updated Slovenian translation 2011-10-25 21:00:45 +02:00
d74721f229 main: Mute the browser plugin 2011-10-25 13:15:05 -04:00
1aa97b19f7 Stop using APIs deprecated in Clutter master
https://bugzilla.gnome.org/show_bug.cgi?id=662627
2011-10-24 17:18:26 -04:00
95de48e986 ShellApp: Junk last_used_time
Instead of saving the last_used_time per-app, grab the maximum time for all
windows. The logic is less hard to keep track of, and it solves some edge
case issues where windows that no longer exist update the user time, even
if none of the other windows have been used recently.

https://bugzilla.gnome.org/show_bug.cgi?id=660650
2011-10-24 16:22:32 -04:00
a147d0428d Updated Persian translation 2011-10-24 21:12:21 +03:30
90b3f7b7f6 Updated Turkish translation 2011-10-24 20:38:49 +03:00
c80acfda08 shell: Remove shell-arrow
It was unused and used deprecated Clutter APIs.
2011-10-24 12:35:01 -04:00
8a39145e3c Replace deprecated GDK functions
https://bugzilla.gnome.org/show_bug.cgi?id=662245
2011-10-24 18:31:22 +02:00
b62f5ef07d telepathy-client: Replace shell_util_new_from_string
The function has been removed in commit 786cfbd397, but one user
was overlooked when replacing it.

https://bugzilla.gnome.org/show_bug.cgi?id=661231
2011-10-24 16:37:15 +02:00
bd5c6c5cd6 Updated Norwegian bokmål translation 2011-10-24 09:36:56 +02:00
f874a57439 Updated Spanish translation 2011-10-23 20:30:39 +02:00
6a4525e554 Updated Spanish translation 2011-10-23 13:55:23 +02:00
d553a5bdc0 Updated Telugu Translation 2011-10-23 12:14:31 +05:30
80076965a7 Updated Lithuanian translation 2011-10-22 22:57:07 +03:00
84e8d38d4c Updated Hebrew translation. 2011-10-22 11:53:15 +02:00
33f3f9d997 Updated Hebrew translation. 2011-10-22 11:16:40 +02:00
be72b1d066 st-texture-cache: Unref each texture when we destroy the async load data
We ref the textures when we add them to the list, so we should unref
them when we destroy the list.
2011-10-21 17:43:11 -04:00
dc5d2b83ef user-menu: Hide "Switch user" on single user machines
Ignoring remote logins, the "Switch user" action is meaningless for
single user setups. Do not show it in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=657011
2011-10-21 22:41:04 +02:00
3dabe645c2 Updated Bulgarian translation 2011-10-21 23:36:04 +03:00
e9ede362dc Add context to ambiguous strings in end session dialogs
Fixes https://bugzilla.gnome.org/show_bug.cgi?id=658664
2011-10-21 22:29:13 +02:00
01357aca35 lookingGlass: make it look consistent with the rest of the Shell
https://bugzilla.gnome.org/show_bug.cgi?id=650900
2011-10-21 14:54:13 -04:00
c944dd6768 theme: Fix indentation
https://bugzilla.gnome.org/show_bug.cgi?id=650900
2011-10-21 14:54:05 -04:00
ff01ed5e4b lookingGlass: add Ctrl+PageUp/PageDown key shortcuts for switching tabs
The view selector in the overview does it too, so why not here?

https://bugzilla.gnome.org/show_bug.cgi?id=652223
2011-10-21 14:51:55 -04:00
d23c374326 Revert "Add Ctrl+PageUp/PageDown key shortcuts for switching tabs"
This reverts commit a69ebc8a68.

This was accidentally the old change.
2011-10-21 14:50:49 -04:00
a69ebc8a68 Add Ctrl+PageUp/PageDown key shortcuts for switching tabs
https://bugzilla.gnome.org/show_bug.cgi?id=652223
2011-10-21 09:22:15 -04:00
f4d8a35b9d altTab: Don't refuse to work when a pointer grab is in place
Allow push_modal to optionally only work with a keyboard only grab and
use that in altTab as a fallback to allow switching windows while a pointer grab
is in effect (like during DND operations).

https://bugzilla.gnome.org/show_bug.cgi?id=660457
2011-10-21 09:12:17 +02:00
2b140f8fb7 Updated Telugu Translation 2011-10-21 10:47:02 +05:30
ab603bdbbf Updated Telugu Translation 2011-10-21 10:47:02 +05:30
44e2f7f555 gnome-shell-extension-tool: Add facilities to enable/disable extensions
https://bugzilla.gnome.org/show_bug.cgi?id=661815
2011-10-20 17:38:48 -04:00
fd1b3b4fee extensionSystem: Rebase the extension order list to help prevent conflicts
When two extensions monkey-patch the same area, enable() and disable() may
behave badly and completely wreck things. To solve this, when disabling
an extension, "rebase" the extension list so that monkey patches should be
added and removed in order.

https://bugzilla.gnome.org/show_bug.cgi?id=661815
2011-10-20 17:38:48 -04:00
4ae2a0b2a5 extensionSystem: Only load importers for enabled extensions
Rather than loading and enabling all extensions at Shell init time, save some
time and gain some basic security by not loading extensions if they're
not enabled.

https://bugzilla.gnome.org/show_bug.cgi?id=661815
2011-10-20 17:38:48 -04:00
0cb415b3bd user-menu: Add missing semi-colons 2011-10-20 22:55:05 +02:00
c1fa9a82e6 docs: Allow building API documentation
As extensions.gnome.org starts to shape up, we should allow to
build the API documentation for St/Shell.

https://bugzilla.gnome.org/show_bug.cgi?id=661045
2011-10-20 22:48:44 +02:00
dde124ab5a user-menu: Minor style fix
Use cameCase for properties/methods defined in JS and under_score
for properties/methods imported from C.
2011-10-20 22:48:44 +02:00
668920cec4 Small coding style fixes 2011-10-20 22:42:30 +02:00
9b38c5b304 Minor documentation fixes 2011-10-20 16:26:07 -04:00
e63c2da433 gnome-shell-extension-tool: Use xdg-open
gnome-open is deprecated
2011-10-20 15:39:14 -04:00
38c768fdb3 gnome-shell-extension-tool: Fix error after creating extension
Commit 7a8a00c705 cleaned up the code to move all
files to a dictionary, which accidentally left an undefined "extensionjs_path"
error. Fix that error.

https://bugzilla.gnome.org/show_bug.cgi?id=661623
2011-10-20 15:38:02 -04:00
0dd4584157 st-texture-cache: Rearrange code to prevent some work and a memory leak
For some reason, the texture cache decides to make a request and then look up
an icon in the icon theme. If it's valid, it just returns, fine, but if it
doesn't add the icon, it tries to undo the request, leaking an
AsyncTextureLoadData that isn't freed in the process.

https://bugzilla.gnome.org/show_bug.cgi?id=660968
2011-10-20 15:26:41 -04:00
b7bf712b97 st-texture-cache: Merge strategies
Rather than have five or six structs allocated duplicating data,
just keep one and simplify the code considerably.

Again, part of my ongoing quest to merge St and Mx.

https://bugzilla.gnome.org/show_bug.cgi?id=660968
2011-10-20 15:19:00 -04:00
615723d8df IMStatusChooserItem: clean up signal handlers on destroy()
Extensions (like alternative-status-menu) expect that calling
destroy() on a menu item will not leave signal handlers around.

https://bugzilla.gnome.org/show_bug.cgi?id=660520
2011-10-20 15:06:52 +02:00
de352a309d global: drop incorrect memset
shell_global_get_memory_info tries to zero initialize the output
parameter with memset, but it passes the wrong size (because of
a missing *).  There's no reason to do the memset, though. In the
normal case all members of the struct gets initialized before the
function returns anyway.

This commit drops the memset call in favor of one explicit 0 assignment
that only gets executed on on atypical platforms.

https://bugzilla.gnome.org/show_bug.cgi?id=662236
2011-10-19 17:13:20 -04:00
c573e7f9a1 global: add missing break statement
Just a drive by fix.

https://bugzilla.gnome.org/show_bug.cgi?id=662235
2011-10-19 17:13:20 -04:00
d59fd1a75d Updated Norwegian bokmål translation 2011-10-19 22:42:59 +02:00
18d69d7032 util: don't depend on the nautilus GSettings schema
Without the desktop, even Nautilus hardcodes "Home" in all places
nowadays (except for the desktop itself). I think a run-time dependency
on nautilus being installed (GSettings will abort if the schema is not
found) is not worth it to keep compatibility with the desktop.

(Also, nautilus itself could probably hardcode "Home" for the desktop as
well and remove the preference).

https://bugzilla.gnome.org/show_bug.cgi?id=659895
2011-10-19 15:37:08 -04:00
5d25716cee st-texture-cache: Use ANSI C-style comments
https://bugzilla.gnome.org/show_bug.cgi?id=660968
2011-10-19 12:40:44 -04:00
840e79c18c Updated Vietnamese translation 2011-10-19 19:39:39 +11:00
ddf562e306 po/vi: import from Damned Lies 2011-10-19 19:39:38 +11:00
20a6ce7003 build: Switch to glib master 2011-10-19 00:26:06 +02:00
8c43298af0 configure: Bump glib requirement
Commit 338ba10ca2 uses unstable API, so adjust the required min version.
2011-10-18 23:51:39 +02:00
4bb48e56d2 Drop deprecated g_thread_init call
GThread will automatically initialize at program start now.

https://bugzilla.gnome.org/show_bug.cgi?id=662011
2011-10-18 16:13:55 -04:00
338ba10ca2 shell-recorder-src: Statically init mutexes instead of using the deprecated API
https://bugzilla.gnome.org/show_bug.cgi?id=662011
2011-10-18 16:13:50 -04:00
36eb745ecc PlacesManager: fix .gtk-bookmarks monitor scope
The GFileMonitor on ~/.gtk-bookmarks was block-scoped in the
PlacesManager._init() function, which caused it to be destroyed as soon
as the constructor was done. This caused changes in bookmarks to never
be notified to possible watchers (such as the places-menu extension).
To fix this, the 'monitor' variable has been promoted to an object
instance member.

https://bugzilla.gnome.org/show_bug.cgi?id=661921
2011-10-18 14:03:36 -04:00
b07e45e214 lookingGlass: Add an easier way to see extension errors
Use the extensions tab to show errors belonging to each extension.

https://bugzilla.gnome.org/show_bug.cgi?id=660546
2011-10-18 13:13:48 -04:00
bba5198e63 Updated Spanish translation 2011-10-18 16:13:19 +02:00
9e2bab008a Updated Norwegian bokmål translation 2011-10-18 07:48:09 +02:00
5ea032bbf7 Bump version to 3.2.1
Update NEWS
2011-10-18 00:23:51 -04:00
897fadfb40 Require Mutter 3.2.1
This is needed for meta_display_unmanage_screen()
2011-10-18 00:19:32 -04:00
b05f71eb9b messageTray: Add banner body when setting the image for a notification
We always place the banner in the body if the notification has additional
content.

https://bugzilla.gnome.org/show_bug.cgi?id=659158
2011-10-18 00:16:25 -04:00
4ce0e80956 user-menu: Be more cautious about saving status
When requesting a presence change, the actual presence set by
mission control does not necessarily match the requested presence
(if an active account does not support the requested presence),
which may result in the wrong presence being restored.
As a fix, be more cautious about saving status by assuming that
users do not request presence changes between an automatic presence
change request and the actual change.

https://bugzilla.gnome.org/show_bug.cgi?id=661485
2011-10-18 04:25:57 +02:00
577ccc4d56 Unmanage the screen before reexecing
This ensures a 'clean shutdown' of mutter.

https://bugzilla.gnome.org/show_bug.cgi?id=660848
2011-10-17 21:55:13 -04:00
39d12351ba gdm: move focus to first item in list
This allows the user to just hit when the user
list first comes up (in many cases).

https://bugzilla.gnome.org/show_bug.cgi?id=657996
2011-10-17 20:49:04 -04:00
e37bc6d7f0 Updated Galician translations 2011-10-18 00:05:07 +02:00
9593ff3582 notificationDaemon: only display a large image if an icon is also specified
Historically, when applications set "image-data" they expect it to show up
as an icon. So we display it as such if an icon is not specified with an
"app_icon" argument to Notify(). We also use "image-path" for an icon if
an icon is not specified.

We only display a large image specified with "image-data" or "image-path"
if an icon is also specified.

https://bugzilla.gnome.org/show_bug.cgi?id=659158
2011-10-17 17:53:16 -04:00
ad8dfd7b04 Updated Hungarian translation 2011-10-17 23:44:33 +02:00
ca5ab20f67 gdm: don't clear bullets while authenticating
Users don't expect the bullets they just typed into an entry
field to disappear as soon as they hit enter.

Instead, they want the dialog to become insensitive during the
authentication process, so that it's clear that what they typed
in is being processed.

https://bugzilla.gnome.org/show_bug.cgi?id=657894
2011-10-17 17:37:06 -04:00
0eab448221 css: Style the capslock warning in password entries
https://bugzilla.gnome.org/show_bug.cgi?id=660806
2011-10-17 22:43:47 +02:00
a26a77f9db st-entry: Display a capslock warning in password entries
Implement the GtkEntry behavior of showing a warning icon when
capslock is turned on while entering hidden text.

https://bugzilla.gnome.org/show_bug.cgi?id=660806
2011-10-17 22:43:47 +02:00
c3df6bb8bd [l10n] Updated German translation 2011-10-17 22:07:46 +02:00
ce3a26cf37 Updated Polish translation 2011-10-17 22:04:00 +02:00
04482c23c4 Updated POTFILES.in 2011-10-17 22:02:37 +02:00
ff20fe856e keyboard: show the keyboard immediately when the user toggles it on
Enabling the keyboard currently doesn't give much notification. Make
it so that the keyboard shows right away when it first gets turned on.

https://bugzilla.gnome.org/show_bug.cgi?id=659743
2011-10-17 14:55:17 -04:00
12e3921f81 messageTray: only set stage input mode when necessary
The message tray focus grabbing code sets the stage input
mode to Shell.StageInputMode.FOCUSED when the overview is
not visible. This ensures the stage window's input
region gets reshaped to include the notification chrome,
and so that input events get delivered appropriately to
the notification that grabbed focus.

The message tray code never tries to restore the stage input
mode later. Instead, the code relies on the stage input
mode (and input region) getting reset to
shell.StageInputMode.NORMAL automatically when focus moves
back from the shell chrome to a window in the user's session.

It's not really correct to set the stage input mode based
on the overview's visibility, though. At the login screen,
even though no overview is visible, the stage input mode is
Shell.StageInputMode.FULLSCREEN which is sufficient
for the notification's needs,  Furthermore,
Shell.StageInputMode.FOCUSED is insufficient for the login
dialog's needs since the login dialog isn't considered
part of the shell's chrome and won't get included in the
stage input region.

This commit changes the message tray code to only set the
stage input mode if the current stage input mode isn't good enough,
rather than assuming the input mode isn't good enough just because
the overview is hidden.

https://bugzilla.gnome.org/show_bug.cgi?id=660919
2011-10-17 13:24:12 -04:00
928fbee15b altTab: fix app ordering in certain edge cases
Because we were sorting the Alt+Tab list by user_time rather than
stacking order / MRU, it was possible for the currently-focused window
to sometimes not be the first app in the list. Fix this by using
meta_display_get_tab_list() to get the proper MRU ordering of windows
on the current workspace, and then convert that to an ordered list of
apps.

https://bugzilla.gnome.org/show_bug.cgi?id=645026
2011-10-17 12:54:03 -04:00
a103c028f9 gdm: add prelight to Not Listed? button
The prelight makes it clearer that the button is
clickable, and makes it more consistent with other
clickable parts of the dialog.

https://bugzilla.gnome.org/show_bug.cgi?id=659763
2011-10-17 11:55:46 -04:00
775347d865 Updated Japanese translation 2011-10-18 00:54:05 +09:00
6d0be86a4e dash: Add minor optimization to _adjustIconSize()
In case _adjustIconSize() is called while the the dash icons are
animating, some extra work is required to yield the expected result.
Skip those extra steps when the icons are not actually animating.

https://bugzilla.gnome.org/show_bug.cgi?id=649248
2011-10-17 16:07:52 +02:00
a4b69db8af dash: Rework _adjustIconSize()
The current code uses the dash's height and current icon size to
calculate the new icon size. However, the height does not correctly
relate to the icon size while the icons are animating, in which
case the resulting icon size may be wrong.
Rework the function to be independent from the actual icon sizes,
so that a correct size is calculated even when called during an
animation.

https://bugzilla.gnome.org/show_bug.cgi?id=649248
2011-10-17 16:07:52 +02:00
790b9d3371 dash: Ignore hiding items in _adjustIconSize()
Rather than relying on the caller to hide the remove target and
removed items before calling _adjustIconSize(), move that logic
into _adjustIconSize() itself.

https://bugzilla.gnome.org/show_bug.cgi?id=649248
2011-10-17 16:07:52 +02:00
adef2009a5 Revert "dash: Ignore hiding items in _adjustIconSize()"
Ooops, a patch got lost in rebase.

This reverts commit b07f9932db.
2011-10-17 16:01:19 +02:00
1721db6d8d Revert "dash: Add minor optimization to _adjustIconSize()"
Ooops, a patch got lost in rebase ...

This reverts commit 6d95e8b988.
2011-10-17 16:00:52 +02:00
6d95e8b988 dash: Add minor optimization to _adjustIconSize()
In case _adjustIconSize() is called while the the dash icons are
animating, some extra work is required to yield the expected result.
Skip those extra steps when the icons are not actually animating.

https://bugzilla.gnome.org/show_bug.cgi?id=649248
2011-10-17 15:58:25 +02:00
b07f9932db dash: Ignore hiding items in _adjustIconSize()
Rather than relying on the caller to hide the remove target and
removed items before calling _adjustIconSize(), move that logic
into _adjustIconSize() itself.

https://bugzilla.gnome.org/show_bug.cgi?id=649248
2011-10-17 15:58:16 +02:00
9439da81c4 Add context menus to some entries
Use ShellEntry.addContextMenu() to add context menus to most
existing entries, with the exception of:

 - the login dialog - it may act be used to enter either the
                      username (e.g. no password entry) or the
                      password, and copy/paste does not make sense
                      (nowhere to copy from, nowhere to paste to)
 - notifications    - while adding a context menu is useful here as
                      well, it will require changes to the tray's
                      focus grab handling, so leave those entries
                      out for now

https://bugzilla.gnome.org/show_bug.cgi?id=659275
2011-10-17 15:29:32 +02:00
6257e64d03 shell-entry: Add API to support entry context menus
Add addContextMenu() to support context menus on right-click/long-press.
Depending on the parameters passed, the context menu only contains
"Copy"/"Paste" actions or an additional "Show/Hide Text" toggle action
for password entries.

https://bugzilla.gnome.org/show_bug.cgi?id=659275
2011-10-17 15:29:32 +02:00
ba110f2d2e network-agent: Remove "Show password" switch
While the ability to show the password can be useful at times,
the existing implementation is problematic:

  1) The use of a switch is wrong (as even noted in a code
     comment).

  2) It is inconsistent with any other password dialog (login screen,
     polkit).

In lack of a properly designed solution (for all password dialogs),
the designers agreed to remove the switch for now.

https://bugzilla.gnome.org/show_bug.cgi?id=658948
2011-10-17 15:29:32 +02:00
6bdf621d05 Fixed some strings in Brazilian Portuguese translation 2011-10-17 10:38:23 -02:00
1c4db98c95 added Kurdish translation 2011-10-17 14:28:49 +02:00
dadac957e4 updated Kurdish translation 2011-10-17 13:38:39 +02:00
cf3976d496 l10n: updated Italian translation 2011-10-17 10:31:24 +02:00
82ed80c9c3 extensionSystem: Load user extensions after system ones
https://bugzilla.gnome.org/show_bug.cgi?id=661815
2011-10-16 14:54:17 -04:00
67222525ad Updated Korean translation 2011-10-17 03:00:56 +09:00
fff0861773 Updated French translation 2011-10-16 12:50:22 +02:00
13ffe41498 Updated Persian translations 2011-10-15 16:05:29 +03:30
96e0528a7b Updated Bulgarian translation 2011-10-15 10:43:14 +03:00
3df30fbd57 Updated Bulgarian translation 2011-10-15 10:00:14 +03:00
b57d8b336b Updated British English translation 2011-10-14 19:12:28 +01:00
3169b2c440 keyboard: fix the keyboard hiding when an extended key is selected
Add a corner case for when the extended key is clicked in order to stop the keyboard
from prematurely closing.

https://bugzilla.gnome.org/show_bug.cgi?id=661707
2011-10-13 16:12:16 -04:00
6bc34e0f32 shell-app: Plug a small memory leak 2011-10-13 22:09:39 +02:00
cecb1a41fb st-texture-cache: Fix a minor crash when computing our border-radius
If we add a 0-sized actor with a border-radius, we will crash as we try to
allocate a 0-sized texture in Cogl. Bail out early instead of doing that.

https://bugzilla.gnome.org/show_bug.cgi?id=661617
2011-10-13 14:15:18 -04:00
b9069df85c search: fix keyboard hiding when user starts a new search
The keyboard hides prematurely when the user is typing into
an empty search box because the click is a captured event that
triggers a loss of entry focus. By adding a keyboard check to
this event, the problem is solved.

https://bugzilla.gnome.org/show_bug.cgi?id=661340
2011-10-13 14:12:21 -04:00
aee3c6f041 shell-app: Remove MATCH_MULTIPLE_{PREFIX,SUFFIX}
We originally OR'ed search terms and favored results which matched
multiple times to get more relevant results. When changing search
to AND search terms, the semantics of "multiple matches" were
changed to refer to a single term matching multiple criteria (name,
executable), which seemed like a good idea at the time.

However in practice this just results in applications whose
user-visible name matches the executable name on disk being
favored over applications using a more generic name, which
isn't too useful (in particular when taking usage frequency
into account).

https://bugzilla.gnome.org/show_bug.cgi?id=623372
2011-10-13 17:46:45 +02:00
c427bba9f1 shell-app: Improve prefix matches
Currently we use a very strict definition of "prefix", where the
search term has to match at the very beginning of the searched
criteria (application name, executable name). Use a more liberal
definition by including matches where the preceding character is
a space (application name) or hyphen (executable name) as well;
as many applications use a prefix, this should improve the quality
of results.

https://bugzilla.gnome.org/show_bug.cgi?id=623372
2011-10-13 17:46:45 +02:00
da83ad561b app-system: Consider usage frequency in search results
Application search results are internally categorized in four sets,
multiple and single prefix matches and multiple and single substring
matches. Each set is currently sorted alphabetically by application
name when concatenating the sets to the final result.
Change the last step to sort each set by usage frequency instead,
which is more likely to favor the most relevant match than
"arbitrary" alphabetic order.

https://bugzilla.gnome.org/show_bug.cgi?id=623372
2011-10-13 17:46:45 +02:00
85520e34ab popup-menu: Allow adjusting where in the source the arrow points to
Currently BoxPointer/Menus always point to the center of the
associated source actor. This is generally what we want, but
add some API to adjust that behavior for the cases where it
isn't.

https://bugzilla.gnome.org/show_bug.cgi?id=659274
2011-10-13 15:03:32 +02:00
d0edd970e1 gdm: clean up spacing
There's a lot of dead vertical space right now from
the session list, even if there are no sessions.

This commit mops that up.

https://bugzilla.gnome.org/show_bug.cgi?id=661479
2011-10-11 17:27:42 -04:00
8529ca70af gdm: don't show fingerprint message right away
Right now we show "(or swipe finger)" at the user login prompt
any time we detect a fingerprint reader.

Checking for the presense of a fingerprint reader isn't really
sufficient for knowing if it is appropriate or not to show the
message, though. Often, a user's fingerprint won't be enrolled
in the system even if the machine has a fingerprint reader.

In this scenario, we end up in a situation where the code will
fade out the message right after fading it in, or worse, fade
out the message while fading it in.

The former case looks flickery and bad, and the latter case
causes the login dialog to lock up since it never completes its
"show prompt" animation and we don't procede with the login
process until after that animation.

If a user is enrolled in the system, the fingerprint pam module
tries to tell the user to swipe their finger.  We never show the
user that message because it's redundant with our own "(or swipe
finger)" message and because it uses techy words like "UPEK" and
"TouchStrip".

This commit changes the code to defer showing "(or swipe
finger)" until the fingerprint pam module forwards us its own
message. This makes it less likely we'll show the message when
fingerprint login won't work, and also removes the fingerprint
animation from the critical path "show prompt" animation.

https://bugzilla.gnome.org/show_bug.cgi?id=660492
2011-10-11 17:25:44 -04:00
1a8d78212f layoutManager: Ignore 1px overlap in _isAboveOrBelowPrimary()
Nvidia's twin view option does not align monitors properly, but with
a one pixel overlap. It looks safe to ignore an overlap this small
to make this case work.

https://bugzilla.gnome.org/show_bug.cgi?id=661387
2011-10-11 16:20:12 +02:00
4270a2806d Updated Hungarian translation 2011-10-11 16:12:28 +02:00
4333bdc709 *.[ch]: add emacs modeline to C files that were missing it
(excluding files that are synced from another module)

https://bugzilla.gnome.org/show_bug.cgi?id=660358
2011-10-11 08:05:17 -04:00
75b824d032 *.js: Make emacs modelines consistent
js2-mode is no longer developed and we recommend js-mode these days,
so switch the modelines to specify that, and make them consistent
across all files.

https://bugzilla.gnome.org/show_bug.cgi?id=660358
2011-10-11 08:05:12 -04:00
7bc2573d85 window-clone: Use ClutterClickAction
Right-click menus in the dash can be dismissed by clicking anywhere
outside the menu. However, if a window clone is located beneath the
pointer when doing so, the window is activated and the overview
closed.
The cause of this unexpected behavior is that window previews are
activated on button-release, which is delivered to the preview after
the menu releases its grab on button-press. Use a ClutterClickAction
instead and let Clutter do the right thing, i.e. only trigger a
'clicked' signal when a button-release event is matched by a
corresponding button-press event.

https://bugzilla.gnome.org/show_bug.cgi?id=661151
2011-10-11 12:56:34 +02:00
67b7b7a950 shell-util: Fix a bogus annotation
Creating a new instance is not (transfer none), unless I'm missing
somehting here...

https://bugzilla.gnome.org/show_bug.cgi?id=661231
2011-10-11 00:07:31 -04:00
786cfbd397 shell-util: Remove shell_util_icon_from_string
GJS doesn't need to be able to represent interfaces for you to be able to
access an interface method, so Gio.icon_new_for_string works fine.

https://bugzilla.gnome.org/show_bug.cgi?id=661231
2011-10-11 00:07:31 -04:00
9df8b583cf Updated Telugu Translations 2011-10-10 16:53:43 +05:30
8e32290dc9 Change default of saved-im-presence to Offline
Unset is not a valid presence type so the Shell shouldn't try setting it when
starting for the first time.

https://bugzilla.gnome.org/show_bug.cgi?id=661272
2011-10-09 15:23:59 -04:00
23478f3336 Updated Lithuanian translation 2011-10-09 00:40:32 +03:00
2e244ecb63 Updated Latvian translation. 2011-10-08 22:48:44 +03:00
e307680206 [l10n] Updated German translation 2011-10-08 20:38:19 +02:00
6bb1a3e2c4 Updated Russian translation 2011-10-08 11:34:28 +04:00
8a1ac6b13f Updated Dutch translation by Wouter Bolsterlee 2011-10-08 02:04:37 +02:00
61b8af2252 Add translator comment for button label 2011-10-08 01:50:20 +02:00
c99afed012 messageTray: Fix accidental typo 2011-10-07 16:34:04 -04:00
2947b92148 user-menu: Restore previous session presence at startup
Save the session presence in GSettings and restore it on startup.

https://bugzilla.gnome.org/show_bug.cgi?id=659021
2011-10-07 07:24:21 +02:00
39c5d23a87 user-menu: Restore previous IM presence on startup
Move the saved user-set presence to GSettings, so it is preserved
between logins.

https://bugzilla.gnome.org/show_bug.cgi?id=659021
2011-10-07 07:24:21 +02:00
d839670f54 Updated Spanish translation 2011-10-06 23:39:36 +02:00
0d1b7e15d1 Updated Swedish translation 2011-10-06 08:09:28 +02:00
ac678f63ee Updated Galician translations 2011-10-06 02:52:18 +02:00
d862c0879b telepathyClient: Check for a no-op before pushing an alias change message
tp-glib can sometimes emit a notify::alias signal when the alias doesn't
actually change. Bail out early instead of pushing an alias change message.

https://bugzilla.gnome.org/show_bug.cgi?id=660774
2011-10-05 15:34:40 -04:00
23a4d4c69e texture-cache: Don't share requests for uncachable textures
Make create_texture_and_ensure_request() aware of the caching
policy to avoid returning the same texture for different
images.

https://bugzilla.gnome.org/show_bug.cgi?id=660585
2011-10-05 21:30:27 +02:00
472b20d933 ShellContactSystem: Make address search actually work
The code was not dealing properly with what folks returns
for im and email addresses.

https://bugzilla.gnome.org/show_bug.cgi?id=660925
2011-10-05 20:26:39 +02:00
492dd718fb ShellContactSystem: make search terms conjunctive
Require that all terms match. This is the expected behaviour
and matches what gnome-contacts does. Keep the prefix/infix
weights in place for now.

https://bugzilla.gnome.org/show_bug.cgi?id=660912
2011-10-05 20:26:38 +02:00
0d5618fdd1 contact-display: Try harder to display a meaningful name
We now match individuals on other properties than alias, so take
this into account when representing a contact in search results
to avoid having them show up as "Unknown".

https://bugzilla.gnome.org/show_bug.cgi?id=660580
2011-10-05 20:26:38 +02:00
503508af41 contact-system: Add helper method to get a (display) email
Folks uses collection/set objects from libgee to store email addresses
associated with an individual. Unfortunately to extract addresses, parts
of libgee which are unusable from (introspected) bindings have to be
used[0], so add a helper method.

[0] in particular gee_iterator_get(), which is annotated as
    "return: (transfer full): gpointer"

https://bugzilla.gnome.org/show_bug.cgi?id=660580
2011-10-05 20:26:38 +02:00
4ec5e55122 Move shell contacts search closer to gnome-contacts
Match folks' name and nick fields, in addition to alias,
and look at email addresses in addition to im addresses.
This is more in line with what gnome-contacts does.

To match this new usage, rename the ALIAS_..._WEIGHT and
IM_..._WEIGHT constants to NAME_ and ADDR_, respectively.

https://bugzilla.gnome.org/show_bug.cgi?id=660580
2011-10-05 20:26:38 +02:00
8daca865ca Updated Slovenian translation 2011-10-05 19:59:01 +02:00
f9b37a21e8 StThemeNodeDrawing: Remove useless LoadCornerData
Done as part of my quest to merge Mx and St:

https://github.com/magcius/mx/tree/st-rebase

https://bugzilla.gnome.org/show_bug.cgi?id=660968
2011-10-05 12:24:28 -04:00
c398f319fc [l10n]Updated Catalan translation 2011-10-04 23:30:47 +02:00
1e049f88b8 Updated Polish translation 2011-10-04 23:25:07 +02:00
4831d9c3a3 lookingGlass: Fix referencing undefined variables 2011-10-04 16:50:58 -04:00
f13f5bc1bb Use '' for non-translated strings
Pay attention to the style guidelines.

https://bugzilla.gnome.org/show_bug.cgi?id=660600
2011-10-04 16:47:53 -04:00
4e114107ed keyboard: Add a missed translation
The word "tray" should be translated to other languages.

https://bugzilla.gnome.org/show_bug.cgi?id=660600
2011-10-04 16:47:53 -04:00
0ccb280008 st-theme-node-drawing: Fix centering when the image needs to be scaled
The translate coordinates are calculated as the offset after the scale, so it
needs to be applied after the scale as well. This fixes random centering issues
in the UI.

https://bugzilla.gnome.org/show_bug.cgi?id=660674
2011-10-04 15:46:01 -04:00
28c3e0693e workspaces-view: Remove window dnd between workspaces
When workspace "previews" in the overview were just tiny gray
rectangles, it made sense to provide a way to move windows
directly between workspaces (by switching workspaces when dragging
a window to the corresponding screen edge). As the overview has
evolved however, the workspace switcher provides a good and
intuitive drop target already, so the alternative provided by the
screen edges is no longer necessary. As it also conflicts with
moving windows between monitors when using a vertical layout,
just remove it.

https://bugzilla.gnome.org/show_bug.cgi?id=660838
2011-10-04 20:30:20 +02:00
c321c8a02f Updated Polish translation 2011-10-04 20:09:02 +02:00
2407ec7b47 Revert "lookingGlass: Add an easier way to see extension errors"
This reverts commit 1e6b824ede.
2011-10-04 13:15:10 -04:00
70eeb75716 lookingGlass: Show extension state in gnome-shell
We translate and create an actor to show the extension state, but we never
actually add it anywhere. Fix that.

https://bugzilla.gnome.org/show_bug.cgi?id=660494
2011-10-04 13:14:54 -04:00
751d250471 autorun: fix a typo in a variable name
The variable |type| doesn't exist here; what we want to do is using the
first member of the contentTypes array instead.
Probably a leftover of some refactoring of the code I did while working
on this.

This patch fixes starting of the default application for a given content
type if the control-center panel is set to run it when a device is
plugged.

https://bugzilla.gnome.org/show_bug.cgi?id=660821
2011-10-03 16:34:59 -04:00
c28217db80 Updated Esperanto translation 2011-10-03 20:40:41 +02:00
0968e556fa WindowDimmer: Make effect private
There is no need for making this public as it is only accessed from within
WindowDimmer.
2011-10-03 18:22:58 +02:00
130f2cf808 WindowDimmer: Don't try to use a ShaderEffect when GLSL is not available
This obviously won't work anyway but will just spam stderr with warnings,
so don't do it.
2011-10-03 18:22:47 +02:00
4eec7413c7 Revert "lookingGlass: Show extension state in gnome-shell"
This reverts commit 32dc24c59b, as it caused
a string break.

https://bugzilla.gnome.org/show_bug.cgi?id=660494
2011-10-02 12:05:21 -04:00
efc3246d26 Updated Bulgarian translation 2011-10-02 13:52:37 +03:00
9930dbc0ff Updated Galician translations 2011-10-02 00:24:03 +02:00
754f87dac3 Updated Swedish translation 2011-10-01 21:57:10 +02:00
51139bd096 Updated Irish translation 2011-10-01 13:26:46 -06:00
6e0119d620 Updated Irish translation 2011-10-01 13:16:56 -06:00
a3528bf973 layout: Fix the actor tracking parameter parsing
It's not appropriate to inherit from the parent property if we pass "false"
as a value, especially when all current parameters are booleans.

https://bugzilla.gnome.org/show_bug.cgi?id=660608
2011-10-01 14:41:53 -04:00
77c36af588 [l10n]Updated Catalan (Valencian) translation 2011-10-01 16:04:25 +02:00
69c0a52a33 [l10n]Updated Catalan translation 2011-10-01 16:04:18 +02:00
a7442cd0a5 Updated Vietnamese translation 2011-10-01 10:31:24 +10:00
d47a013931 vi.po: import from Damned Lies 2011-10-01 10:29:31 +10:00
745f9c0e6e Added asturian language 2011-09-30 21:04:02 +02:00
414f49fd80 Updated asturian translation 2011-09-30 21:02:57 +02:00
e49a595f54 st-texture-cache: Don't cache GIcons which cannot be serialized
For GIcons we use g_icon_to_string() in the key, but the function
will return NULL if the icon cannot be serialized. As a result,
all non-serializable GIcons of the same size end up with the same
cache key - an example for this are contacts with avatars, which
currently all end up with the same image.
To fix, opt out of caching for GIcons which cannot be serialized.

https://bugzilla.gnome.org/show_bug.cgi?id=660585
2011-09-30 20:14:25 +02:00
77485c2a04 [l10n] Updated German translation 2011-09-30 20:07:51 +02:00
e9b28634e5 Updated Slovenian translation 2011-09-30 19:13:24 +02:00
b43dcb8876 layout: Fix setting fullscreen for screen sized windows
We have to set the flag for all monitors in that case.
2011-09-30 18:46:14 +02:00
f0c1eeece8 Updated Spanish translation 2011-09-30 18:27:07 +02:00
1e6b824ede lookingGlass: Add an easier way to see extension errors
Use the extensions tab to show errors belonging to each extension.

https://bugzilla.gnome.org/show_bug.cgi?id=660546
2011-09-30 12:19:30 -04:00
1e2d16273c layoutManager: Treat screen_sized OR windows as fullscreen
https://bugzilla.gnome.org/show_bug.cgi?id=660166
2011-09-29 21:38:05 +02:00
32dc24c59b lookingGlass: Show extension state in gnome-shell
We translate and create an actor to show the extension state, but we never
actually add it anywhere. Fix that.

https://bugzilla.gnome.org/show_bug.cgi?id=660494
2011-09-29 13:15:27 -04:00
7a8a189c48 boxpointer: Don't constrain box pointer to primary monitor
A boxPointer should be able to be attached to any actor, not just ones on the
primary monitor. Assume that the sourceActor doesn't straddle monitors, and
constrain the boxPointer to the monitor the sourceActor is on.

https://bugzilla.gnome.org/show_bug.cgi?id=659861
2011-09-29 13:15:01 -04:00
6aa411fecc keyboard: ignore D-Bus requests when the OSK isn't enabled
Fixes spurious warnings about "this.actor is null" when processing
org.gnome.Caribou.Keyboard messages when the keyboard isn't enabled.

https://bugzilla.gnome.org/show_bug.cgi?id=659940
2011-09-29 10:28:49 -04:00
9c76318df8 main: remove a stray second keyboard.init() call
Originally the keyboard was initialized in the user-session-specific
code, but it was later moved to the generic code. Except that it was
accidentally copied rather than moved.

https://bugzilla.gnome.org/show_bug.cgi?id=659940
2011-09-29 10:28:46 -04:00
6510904711 windowAttentionHandler: Remove "%s has finished starting"
The message is verbose and confusing. Use the traditional "'%s' is ready"
for all cases.

https://bugzilla.gnome.org/show_bug.cgi?id=660310
2011-09-29 09:59:09 -04:00
a9817f4832 automountManager: Don't mount already-mounted volumes
Don't pester the user with autorun popups if a volume is already mounted.

https://bugzilla.gnome.org/show_bug.cgi?id=660397
2011-09-28 16:51:02 -04:00
9067689839 power-status: Use correct DBus signatures
Devices are represented as susdut, not susbut (i.e. the percentage
is a double rather than a boolean) - apparently the wrong signature
works, but correct it anyway.

https://bugzilla.gnome.org/show_bug.cgi?id=660122
2011-09-28 19:56:23 +02:00
4e9e91fdce recorder: Use CoglHandle instead of CoglHandle*
The latter has always been wrong and should have been fixed a
while ago, but somehow we overlooked shell-recorder.

https://bugzilla.gnome.org/show_bug.cgi?id=659822
2011-09-28 19:56:23 +02:00
73cc91ba60 update zh_CN translation 2011-09-28 01:01:12 +08:00
0ab0d0860f Uploaded Ukranian 2011-09-27 14:56:16 +03:00
337b399a75 Updated Serbian translation 2011-09-27 02:47:53 +02:00
46f21e81e3 updated Tamil translation 2011-09-27 05:14:59 +05:30
c1300ddbbc Bump version to 3.2.0
Update NEWS
2011-09-26 16:39:21 -04:00
8bd5b1e696 st: Fix crash in theme-node-transition
Setting up the framebuffers for transitions may fail, in which case
the material used for drawing is left uninitialized, so trying to
access it results in a crash.
Instead bail out in this case, which means that we won't paint
anything during the transition - still, drawing errors are better
than crashes ...

https://bugzilla.gnome.org/show_bug.cgi?id=659676
2011-09-26 19:52:36 +02:00
40c5db397d messageTray: unset this._clickedSummaryItem if we are hiding the summary box pointer and don't have a new clicked summary item
This ensures that this._clickedSummaryItem is always unset correctly.

Because we disconnect the signals that have _adjustSummaryBoxPointerPosition()
as a callback when unsetting this._clickedSummaryItem, we no longer call
setPosition() on this._summaryBoxPointer after it is hidden. Calling
setPosition() shows the box pointer again, which previously resulted in
an empty box pointer staying behind when a notification associated with
a tray icon was clicked.

https://bugzilla.gnome.org/show_bug.cgi?id=659862
2011-09-26 13:30:46 -04:00
8b52919b4d Updated Telugu Translation 2011-09-26 22:46:26 +05:30
2859f23038 Fixed some strings in Brazilian Portuguese translation 2011-09-26 10:36:54 -03:00
00384ccb47 telepathyClient: call delay on context when approving a FT or call
Not doing so is considered as a bug as we don't accept the context right away.
It leads to tp-glib returning directly from the AddDispatchOperation() D-Bus
call and so automatically approve the channel if the Shell is the only
approver running.

https://bugzilla.gnome.org/show_bug.cgi?id=660084
2011-09-26 14:49:21 +02:00
0191cc589a Updated Finnish translation 2011-09-26 15:43:09 +03:00
dae6db9b51 [l10n]Updated Catalan (Valencian) translation 2011-09-26 14:26:05 +02:00
4d912c9feb Updated Japanese translation 2011-09-26 11:59:08 +09:00
07c4b46466 Update Czech translation 2011-09-26 02:00:30 +02:00
e9cdce49a3 Updated Russian translation 2011-09-26 01:18:09 +04:00
84c3ce8778 Updated Latvian translation. 2011-09-25 21:55:16 +03:00
d5a32a7fe4 Updated Japanese translation 2011-09-26 01:04:14 +09:00
cc4dfda21b Updated Indonesian translation 2011-09-25 15:41:58 +07:00
703b37aa7b Updated Traditional Chinese translation(Hong Kong and Taiwan) 2011-09-25 14:52:31 +08:00
b433de9022 Updated Bulgarian translation 2011-09-25 08:34:54 +03:00
0c1a22ff95 Updated Slovak translation 2011-09-24 22:57:48 +02:00
4d526e40c3 [l10n] Updated German translation 2011-09-24 20:46:15 +02:00
4586c7b36b Fix display of dates in the Hungarian translation 2011-09-24 19:18:51 +02:00
3d60b73b60 Updated Lithuanian translation 2011-09-24 15:41:38 +03:00
4ef5cd8ef6 Updated Hungarian translation 2011-09-24 01:46:46 +02:00
6959bd19f1 Updated Basque language 2011-09-23 18:19:56 +02:00
17e4ce5ea8 Updated French translation 2011-09-23 18:08:57 +02:00
33094b4988 messageTray: fix notification/keyboard interaction
If the pointer moves from the notification into the keyboard, don't
treat that as moving out of the tray.

https://bugzilla.gnome.org/show_bug.cgi?id=658603
2011-09-23 07:56:45 -04:00
a8fdcffd44 messageTray: move the summary box pointer up when the keyboard shows
If the keyboard is shown when a summary boxpointer is visible (eg,
when opening a chat source), animate the boxpointer up with the tray.

https://bugzilla.gnome.org/show_bug.cgi?id=658603
2011-09-23 07:55:47 -04:00
5adb5411fa keyboard: fix D-Bus name acquisition flags to totally block Antler
We never want the antler keyboard to run if gnome-shell is running, so
grab org.gnome.Caribou.Keyboard without the "allow replacement" flag.

https://bugzilla.gnome.org/show_bug.cgi?id=659865
2011-09-23 07:53:51 -04:00
ff0d11c89c Updated Hebrew translation. 2011-09-23 08:51:57 +03:00
5f6dce2b5c popup-menu: Fix allocation in RTL locales
Commit ed7d4928e5 fixed some width-for-height cases in popup menu items,
but did not consider RTL locales. Fix this.

https://bugzilla.gnome.org/show_bug.cgi?id=659827
2011-09-22 21:52:26 +02:00
a7405e8b39 Updated translation for Afrikaans (af) not including latest strings since freeze 2011-09-22 21:20:29 +02:00
d87c520bad Updated Esperanto translation 2011-09-22 20:29:31 +02:00
24959f8d34 Updated Esperanto translation 2011-09-22 20:26:25 +02:00
6d1432e166 [l10n]Updated Catalan translation 2011-09-22 19:54:11 +02:00
2c7ba2c125 Updated Korean translation 2011-09-23 02:33:19 +09:00
a0ba664c64 messageTray: only update an icon when necessary on notification update
This avoids unnecessarily removing and resetting the icon in the notifications.

This fixes the new chat notification sliding down and up slightly when new
messages are received.

https://bugzilla.gnome.org/show_bug.cgi?id=659768
2011-09-22 13:25:35 -04:00
131da5f523 telepathyClient: update the avatar correctly when it changes
Previously, when the avatar changed, we would not update the summary icon
for the source at all and would only update the notification icon when the
next message was received. Instead, we should update both immediately upon
recieving the signal that the avatar has changed.

https://bugzilla.gnome.org/show_bug.cgi?id=659768
2011-09-22 13:25:35 -04:00
7c6144450a Updated Slovenian translation 2011-09-22 18:06:45 +02:00
4c7369db16 build: Switch to stable cogl/clutter branches for now 2011-09-22 16:35:45 +02:00
f4355de896 Updated Telugu Translation 2011-09-22 19:56:35 +05:30
4a3db9f44d Updated Danish translation 2011-09-22 16:07:11 +02:00
6c0a9ff9cc Updated Brazilian Portuguese translation. Reviewed by Djavan Fagundes <djavanf@gnome.org>. 2011-09-22 09:59:50 -03:00
57b1695fcf keyboard: don't try to move windows out of the way of the keyboard
This code was never tested very well, and has several problems
currently (windows creeping down and to the right, windows snapping to
a different location after you move them). To be fixed in 3.4.

https://bugzilla.gnome.org/show_bug.cgi?id=659643
2011-09-22 07:55:20 -04:00
045c6546cc Add Assamese translation 2011-09-22 12:19:02 +02:00
ce5bd954bf Add Assamese translation 2011-09-22 12:18:08 +02:00
6ef00a4a3b Updated Spanish translation 2011-09-22 11:47:37 +02:00
569d9718a0 Updated Norwegian bokmål translation 2011-09-22 10:10:41 +02:00
00a3a8697f [l10n] Updated German translation 2011-09-22 08:50:50 +02:00
c93444e390 Updated Swedish translation 2011-09-22 06:38:15 +02:00
5e5788ab14 update Punjabi Translation 2011-09-22 06:16:06 +05:30
abf57e6362 Updated Portuguese translation 2011-09-22 00:35:36 +01:00
74c2074d5a Updated Galician translations 2011-09-21 22:51:32 +02:00
b74606312e Updated Polish translation 2011-09-21 22:05:00 +02:00
0c849df4d5 Updated Belarusian translation. 2011-09-21 22:18:23 +03:00
38690d4a09 autorun: mark string as translatable 2011-09-21 21:10:50 +02:00
b64c237cb3 Updated Polish translation 2011-09-21 20:39:04 +02:00
3fbee8e027 configure.ac: switch to tar-ustar
tar-ustar is the GNOME standard and has some small advantages
over the default tar-v7, like support for long file names.
2011-09-21 12:33:11 -04:00
a68e6e3c63 Updated Danish translation 2011-09-21 17:57:09 +02:00
db5a72774d configure: Require gjs 1.29.18 for GC stuff 2011-09-21 10:04:36 -04:00
966f90f24a Updated Hungarian translation 2011-09-21 03:08:49 +02:00
eaa664c023 Updated Portuguese translation 2011-09-21 00:45:47 +01:00
e9282c3987 Bump version to 3.1.92
Update NEWS
2011-09-20 17:16:24 -04:00
ed1f8ed339 Add missing test to Makefile.am 2011-09-20 17:15:41 -04:00
5d0d637eb8 Updated Serbian translation 2011-09-20 22:14:58 +02:00
7e70dfdf4c messageTray: Fix line wrapping
Clutter 1.4 had a bug where it would wrap when it wasn't supposed to, and we
were unknowingly relying on it. Explicitly pass the available width/height
to get a perfect allocation.

https://bugzilla.gnome.org/show_bug.cgi?id=659633
2011-09-20 16:06:06 -04:00
3f61f39ae3 dash: Adjust placeholder size to icon size
The placeholder looks odd near small icons and causes the dash to get wider
when visible and narrower when hidden.

https://bugzilla.gnome.org/show_bug.cgi?id=659210
2011-09-20 21:01:25 +01:00
371f623a3e Updated Latvian translation. 2011-09-20 22:15:44 +03:00
566d566f26 alt-tab: Do not hardcode ALT modifier
While we allow for arbitrary modifiers in keybindings, both the
alt-tab and ctrl-alt-tab popups close when ALT is not present in
the modifier mask, resulting in ALT being de-facto hardcoded.
Instead, pass the actual modifier mask when invoking the popups.

https://bugzilla.gnome.org/show_bug.cgi?id=645200
2011-09-20 21:07:19 +02:00
fb30822860 windowManager: shade the actor, not the texture
Applying the "dim window" effect to the MetaWindowActor has two avantages:
first it avoids triggering bugs where ClutterOffscreenEffect doesn't handle
clone paint correctly. Second, it avoids showing the window as dimmed in
alt-Tab and the overview, which is weird.

The small downside of this is that the shadow becomes slightly gray when
the window dimmed, which is wrong - if we switched from blending with gray
to a combination of desaturation and darkening, this problem wouldn't
happen.

Revert out the addition of startY to the shader, since we don't need it
and fix the application of alpha, since we need to handle alpha correctly
for the shadow.

https://bugzilla.gnome.org/show_bug.cgi?id=659634
2011-09-20 14:56:25 -04:00
526a53bdd4 shell-network-agent: Do not handle VPN secrets
VPN secrets are currently unhandled by the UI code. To avoid
lengthy timeouts, bail out early with an error, so NetworkManager
falls back to the nm-applet agent directly.

https://bugzilla.gnome.org/show_bug.cgi?id=658484
2011-09-20 20:05:21 +02:00
3833124d66 apps: Uniquify application instances explicitly by id
Commit 0af108211c introduced a
regression where applications that appear in multiple categories were
duplicated in the "All Apps" list, because we switched from
uniquifying on desktop file ID to the GMenuTreeEntry.

Switch back to keeping the set of apps based on ID.  To flesh this
out, we keep the ShellApp instance for a given ID around forever, and
when we're loading new contents, we replace the GMenuTreeEntry inside
the app. That means callers still get new data.

We still keep around the running app list, though we could just
recompute it from the app list now.

https://bugzilla.gnome.org/show_bug.cgi?id=659351
2011-09-20 13:56:53 -04:00
e5e1b52abf apps: Unify code for apps/settings loading more
The apps and settings loading code duplicated the part to traverse a
GMenuTree.  Unify this by adding a new function to return a flattened
set.

This will also be useful for a future change to how we store apps -
this way we can look at both the current set of apps and the new set.

https://bugzilla.gnome.org/show_bug.cgi?id=659351
2011-09-20 13:56:53 -04:00
82fc66305d Clear the active network when removing the active access point
When the active AP disappears, it is possible to receive the
"access-point-removed" signal before the "notify::active-ap" (as
dbus-glib + libnm-glib property notifications are not reliable).
In that case, we would remove the AP from the network object, thus
an attempt to update the UI would create an item for an empty
network.

https://bugzilla.gnome.org/show_bug.cgi?id=658150
2011-09-20 19:01:23 +02:00
05e0d5066f Don't create AP items for empty networks
Current code is sometime attempting to create menu items for wifi
networks that have no visible AP. I have no idea why this is
happening, but it should fix the symptoms and avoid exceptions.

https://bugzilla.gnome.org/show_bug.cgi?id=658150
2011-09-20 19:01:01 +02:00
ce2dc84e49 Updated Slovenian translation 2011-09-20 13:08:28 +02:00
eb14ed32ea Updated Russian translation 2011-09-20 12:02:47 +04:00
f97987d0d8 Updated Norwegian bokmål translation 2011-09-20 09:16:33 +02:00
d23d9c3b05 update Punjabi Translation 2011-09-20 07:22:39 +05:30
3ebf6e3bea autorun: don't use a custom size for the hotplug icon
Instead, just use the default MessageTray.Source icon size (24px).

https://bugzilla.gnome.org/show_bug.cgi?id=658004
2011-09-19 20:53:50 -04:00
9f41f5c740 l10n: Updated Italian translation 2011-09-20 00:17:09 +02:00
ae00f86887 panel: allow padding around panel buttons to shrink
for narrow screens (eg, portrait orientation)

https://bugzilla.gnome.org/show_bug.cgi?id=651299
2011-09-19 17:43:54 -04:00
ab67c0f8b0 userMenu: fix line wrapping
The previous wrapping code hardcoded a width in pixels, making it
non-text-zoom-friendly. Specify a CSS width in pts, and fix the
userMenu code to completely opt out of the popupMenu column behavior.
Hack PopupComboBoxMenuItem slightly to deal with the fact that the
pop-up no longer gets setColumnWidth'ed.

https://bugzilla.gnome.org/show_bug.cgi?id=652837
2011-09-19 17:36:13 -04:00
ed7d4928e5 popupMenu: fix a few width-for-height cases
specifically, non-columned menus, and span==-1

https://bugzilla.gnome.org/show_bug.cgi?id=652837
2011-09-19 17:36:13 -04:00
f23239a923 status/keyboard: use correct style class name to fix menu highlight
The keyboard status item doesn't derive from SystemStatusButton, since
it doesn't use an icon. But this meant it wasn't getting the right
class name, and so was using the full-width menu title highlight
rather than the small one. Fix that.
2011-09-19 17:36:13 -04:00
d949920e68 status/keyboard: fix function naming style 2011-09-19 17:36:13 -04:00
a1def85c18 Updated Latvian translation. 2011-09-19 23:30:31 +03:00
dc624f65e4 user-menu: Fix spacing in combobox item
At some time an additional container was added which broke the
existing CSS rule.
2011-09-19 22:06:31 +02:00
361652d028 popup-menu: Use correct St.Align values
It is not St.Align.CENTER, but St.Align.MIDDLE - in contrast to
St.TextAlign, where it *is* CENTER. Yay consistency!
2011-09-19 22:06:31 +02:00
92024b7e54 network-agent: Allow entries to activate default action
Currently entries' 'activate' signal is ignored, so hitting enter
does not have any effect, even if all required information has been
entered.
Instead, connect to the 'activate' signal so that hitting enter
behaves as if the "OK" button had been pressed.

https://bugzilla.gnome.org/show_bug.cgi?id=659133
2011-09-19 21:26:32 +02:00
4e4ce0dd21 network-agent: Focus the first reactive entry
Currently network dialogs don't focus password entries, which means
that rather than entering their password directly, users first have
to click the entry (or tab around the dialog).
Instead, put keyboard focus on the first entry that requires user
input.

https://bugzilla.gnome.org/show_bug.cgi?id=659133
2011-09-19 21:26:32 +02:00
bc0c490ec3 polkit-agent: Remove hack to focus password entry
As dialog buttons used to "steal" the initial key focus, the polkit
dialog delayed focusing the password entry. With buttons no longer
overwriting the manually set focus, this is no longer necessary.

https://bugzilla.gnome.org/show_bug.cgi?id=659133
2011-09-19 21:26:32 +02:00
6d92af17fd modal-dialog: Don't let buttons steal manually-set focus
ModalDialog provides a method to set the initial focus. However,
when adding buttons, the initial focus is always set to the last
button, thus overwriting a previously set manual focus.
Instead, only set the initial key focus if setInitialKeyFocus()
has not been called manually before.

https://bugzilla.gnome.org/show_bug.cgi?id=659133
2011-09-19 21:26:32 +02:00
2140a498a2 StTable: Silence row_span warning for now
This seems to be very noisy and generally harmless so
silence it up for now.

https://bugzilla.gnome.org/show_bug.cgi?id=658939
2011-09-19 21:25:07 +02:00
28349d362c Updated Belarusian translation. 2011-09-19 21:59:18 +03:00
e7af9f98e3 [l10n] Updated German translation 2011-09-19 20:17:24 +02:00
dd6053c5e1 [l10n] Updated German translation 2011-09-19 20:14:48 +02:00
37726a4cb6 app: Don't abort if a .desktop file has no Exec= key
Seen in the wild in nautilus-pastebin-configurator.desktop.
2011-09-19 14:13:20 -04:00
4352cc231e modalDialog: Don't use a for...in loop for iterating over arrays.
for...in loops do not guarantee that the indexes iterate in order. As a
consequence, buttons may appear in an undesired order.
2011-09-19 13:42:47 -04:00
824220356f popup-menu: Add 'sensitive' flag to item parameters
A menu action may not make sense at any time, so add API to mark
an item insensitive to indicate that its action is currently
unavailable, but may become activatable at a later point.

https://bugzilla.gnome.org/show_bug.cgi?id=659270
2011-09-19 18:54:15 +02:00
247ad9d7ab scroll-view-sizing: Add tests for padding / borders
Add tests to verify that the fade works fine with borders and
padding.

https://bugzilla.gnome.org/show_bug.cgi?id=659159
2011-09-19 18:51:07 +02:00
09fe12d0c1 st-scroll-view-fade: Pass a precomputed fade area to the shader
Instead of doing complex computations in the shader just pass in the correct
fade area (taking padding, scrollbars and rtl into account) and just work
with that in the shader.

That fixes a bug where we would fade the scrollbar when padding is present.

https://bugzilla.gnome.org/show_bug.cgi?id=659159
2011-09-19 18:51:01 +02:00
127ef8383b windowManager: Incorporate invisible borders into window dimming effect
Without this, the dim "fade" will start at the top of the untrimmed actor. With
a large enough draggable_border_width setting, this will show no fade at all.

https://bugzilla.gnome.org/show_bug.cgi?id=659302
2011-09-19 12:39:48 -04:00
82eccb566c windowManager: Use an off-screen buffer for window dimming
The way the window dimmer shader is applied will cause rendering errors with
the rounded corners, invisible borders or shaped textures since it doesn't deal
well with the multitexturing used by the MetaShapedTexture. Use an off-screen
buffer to flatten the texture before being applied.

https://bugzilla.gnome.org/show_bug.cgi?id=659302
2011-09-19 12:39:41 -04:00
543b29efe7 userMenu: Update the user information if the object is already loaded
Any time we get a cached user object from the AccountsService, it will
already be loaded.

https://bugzilla.gnome.org/show_bug.cgi?id=658605
2011-09-19 10:50:54 -04:00
0f4ce5dd4e altTab: Fix icon scrolling
Don't depend on the primary monitor position for determining whether we should
scroll, just use our own container's size.

https://bugzilla.gnome.org/show_bug.cgi?id=658239
2011-09-19 10:50:54 -04:00
c23919df15 Silently add chat source in the MessageTray
We don't want the tray bar to open/close quickly when adding a chat because
it happens when user opens the chat from Empathy. The notification will
popup on incoming message anyway.

https://bugzilla.gnome.org/show_bug.cgi?id=657249
2011-09-19 16:19:26 +02:00
2b2a8b4747 ctrlAltTab: don't allow more than one popup
In a normal user session you can't have more than one
popup, because the popup is modal and we don't allow
the popup to show up when there are other modals.

In a GDM session, however, the login dialog is modal, and
we want a popup, so we don't have that same check.

This commit changes the ctrlAltTab manager code to not
allow multiple popups.

https://bugzilla.gnome.org/show_bug.cgi?id=659177
2011-09-19 10:17:19 -04:00
2e48dbf6ee main: Let SWITCH_PANELS keybinding through at login screen
Users depend on being able to switch focus between the panel
and the login screen using ctrl-alt-tab.

Because the login screen has no overview, we were short circuiting
some code that needs to get run to support ctrl-alt-tab.

This commit changes the short-circuit code to only run for user
sessions.

https://bugzilla.gnome.org/show_bug.cgi?id=659177
2011-09-19 10:17:18 -04:00
207917ba45 Updated Slovenian translation 2011-09-19 16:15:44 +02:00
f824b97f3a Updated Polish translation 2011-09-19 15:07:04 +02:00
a5ff2fc68f Updated Spanish translation 2011-09-19 14:57:34 +02:00
4906806c0b Updated Gujarati Translations 2011-09-19 18:10:00 +05:30
d98336a906 updated Tamil translation 2011-09-19 17:51:28 +05:30
9ddd11cdb8 Updated Galician translations 2011-09-19 13:44:45 +02:00
554ad4ef05 keyboard: switch to using the correct gsettings key for enable/disable
https://bugzilla.gnome.org/show_bug.cgi?id=612662
2011-09-19 07:34:02 -04:00
45d8550044 Updated Galician translations 2011-09-19 13:18:59 +02:00
f2b44c1494 Updated Persian translation 2011-09-19 14:37:17 +04:30
bdb28e87a9 Updated Persian translation 2011-09-19 14:35:13 +04:30
8b9592e53e Updated Slovak translation 2011-09-19 11:30:12 +02:00
84f89648a6 Update Simplified Chinese translation. 2011-09-19 09:25:48 +00:00
1174992099 Updated Spanish translation 2011-09-19 10:35:57 +02:00
391a220dc1 gdm: add optional logo to user list
This commit adds the ability to set a small logo
at the login screen for administrators and
distributions.

https://bugzilla.gnome.org/show_bug.cgi?id=658062
2011-09-19 00:08:55 -04:00
43f1d0578b gdm: add fingerprint support
This commit adds the ability to log in with a fingerprint instead
of a password (assuming the user is enrolled and fingerprint
isn't disabled via gsettings)

https://bugzilla.gnome.org/show_bug.cgi?id=657823
2011-09-18 23:32:03 -04:00
d3e35028ca Updated Hungarian translation 2011-09-19 02:00:57 +02:00
61c7d87003 Updated Hungarian translation 2011-09-19 02:00:57 +02:00
20f2fa84a4 Updated Swedish translation 2011-09-18 19:35:23 +02:00
17e269b96c Updated Latvian translation. 2011-09-18 20:16:21 +03:00
6a57ac92ba Updated Spanish translation 2011-09-18 17:23:31 +02:00
07ad03556f Updated POTFILES.in 2011-09-18 14:09:01 +02:00
04c33230c8 Updated Bulgarian translation 2011-09-18 11:40:55 +03:00
083dac630f updated Tamil translation 2011-09-18 14:05:50 +05:30
6375724196 Updated Vietnamese translation 2011-09-18 15:59:43 +10:00
922957c1ae po/vi: import from Damned Lies 2011-09-18 15:34:15 +10:00
cbf7d0e738 vi/po: removed redudant %d 2011-09-18 15:30:50 +10:00
fb81efeecf theme/gdm: shrink entry
commit 8424236daa
set the entry to a specific size to prevent it from
growing for long passwords.

The size is a little too long though, since it makes
the dialog pop out horizontally when showing the entry.

This commit drops it down a few em.

https://bugzilla.gnome.org/show_bug.cgi?id=659370
2011-09-17 23:40:20 -04:00
8466198626 loginDialog: subtract padding when drawing focus line
If there's no scrollbar in the user list it grows as the
user arrows around.  This is because it wasn't taking
padding into account when computing its destination size.

https://bugzilla.gnome.org/show_bug.cgi?id=658469
2011-09-17 23:25:31 -04:00
33718ef7a5 panel: drop dead code
The _userMenu variable never gets initialized anymore,
so remove some code that only gets run when it's defined.

https://bugzilla.gnome.org/show_bug.cgi?id=651299
2011-09-17 23:23:15 -04:00
a94a62764d gdm: add a power button
Making users have to log in to power off the machine isn't a good idea.

This commit adds a power menu similar to the one in the fallback greeter
which offers 3 items:

- Suspend
- Restart
- Power off

https://bugzilla.gnome.org/show_bug.cgi?id=657822
2011-09-17 23:16:20 -04:00
12c98df3db Updated Traditional Chinese translation(Hong Kong and Taiwan) 2011-09-18 08:35:10 +08:00
81909a406f Updated reduced Finnish translation 2011-09-17 22:29:36 +03:00
d291aaa4f2 Updated Galician translations 2011-09-17 21:21:38 +02:00
da36d87e46 Updated Punjabi Translation 2011-09-18 00:42:12 +05:30
42bc09a7ef Updated Polish translation 2011-09-17 20:55:43 +02:00
d0defc592e Updated Belarusian translation. 2011-09-17 20:15:11 +03:00
700f7fbaf3 gdm: replace 'gdm-password' with a constant
It's a little messy to have 'gdm-password' strung all
through the code, so this commit defines a constant up
top and uses that instead.

https://bugzilla.gnome.org/show_bug.cgi?id=657823
2011-09-17 12:53:54 -04:00
74f2d02ac3 Updated Slovenian translation 2011-09-17 18:15:01 +02:00
c865d96f4b Update Simplified Chinese translation. 2011-09-17 16:04:56 +00:00
cd7b9e108c user-menu: Explain why disabling notifications changes IM status
While the current behavior of setting the IM status to "busy" while
notifications are disabled makes sense, as incoming messages are
very likely to be missed, it is not immediately obvious.
Display a transient notification to explain the behavior to the user.

https://bugzilla.gnome.org/show_bug.cgi?id=652718
2011-09-17 17:18:29 +02:00
f54b82f64c global: Initiate *full* GC at idle
While I've been trying to make the GC kick in more often, I've decided
it's a better tradeoff to aggressively GC at "leisure", for multiple
reasons.

We can and should revisit this at a later time, but basically:

* The shell doesn't generate *that* much JS data - garbage collection
  is very fast here.
* Long periods without GC mean we're not calling free() when we
  could, which in turn makes heap fragmentation much worse.
* Ensuring the GC runs at idle makes it much less likely we'll take
  a random large GC hit in the middle of an animation.

https://bugzilla.gnome.org/show_bug.cgi?id=659254
2011-09-17 10:16:35 -04:00
36bfe8c533 memory: Add display of elapsed seconds since a garbage collection
This is useful information for debugging.

https://bugzilla.gnome.org/show_bug.cgi?id=659254
2011-09-17 10:16:35 -04:00
a40d063cb8 Updated Russian translation 2011-09-17 14:14:52 +04:00
ac88a88bfb Updated Norwegian bokmål translation 2011-09-17 11:47:14 +02:00
d89c9b4556 Updated Japanese translation 2011-09-17 13:07:50 +09:00
a07056f5e2 l10n: Updated Italian translation 2011-09-17 02:01:56 +02:00
f91bea3ea5 Updated Portuguese translation 2011-09-17 01:00:25 +01:00
9fbd79316a panel: merge statusBox into rightBox
Simplify the layout in rightBox by getting rid of statusBox, and just
putting everything into rightBox directly.

Simplify the handling of the user menu by adding it like it was a
status icon rather than special-casing it. Rename the "tray_icon"
variables to "status_area" to reflect this better.

https://bugzilla.gnome.org/show_bug.cgi?id=651299
2011-09-16 14:15:49 -04:00
6d89d0b02a panel: remove legacyBox, mix legacy icons in with regular
Legacy trayicons are mostly gone, so remove some of the special-casing
for them to simplify things.

Also, fix panel.addToStatusArea() to interpret its "position" relative
to tray_icon_order, not relative to the existing contents of
statusBox, so that the order that extension icons appear in does not
depend on the order they are loaded in.

https://bugzilla.gnome.org/show_bug.cgi?id=651299
2011-09-16 14:15:49 -04:00
0febcbfa2a userMenu: belatedly rename CSS to match the new filename
https://bugzilla.gnome.org/show_bug.cgi?id=651299
2011-09-16 14:15:48 -04:00
f2f2898fe3 panel: fix part of the panel-corner-highlighting hack
The underline highlights on the panel menu items normally have a 100ms
transition between highlighted and unhighlighted, but the panel corner
graphics can't do that, so we hacked the Activities button and user
menu to have no transition. But in gdm mode, the user menu isn't the
rightmost item any more. Fix this by modifying the CSS from the code
instead.

https://bugzilla.gnome.org/show_bug.cgi?id=651299
2011-09-16 14:15:48 -04:00
f65826b3ba network: Don't notify on connection lost
The design is for applications to do this, basically.  Web browsers,
Evolution, Empathy already display something on network status change.

Note though we need an application API in GIO to monitor network state;
see https://bugzilla.gnome.org/show_bug.cgi?id=620932

https://bugzilla.gnome.org/show_bug.cgi?id=658954
2011-09-16 13:57:14 -04:00
a869007180 calendar: Improve Layout
- align event header with buttons
 - button hovers stretch across fully like menus
 - better padding for month
 - day hover is lighter

https://bugzilla.gnome.org/show_bug.cgi?id=641135
2011-09-16 19:22:12 +02:00
b5fa48f485 status: don't show 'Show Keyboard Layout' on the login screen
It does not make a great amount of sense to have this function
on the login screen. And worse, it does not work, since the greeter
is currently a modal dialog, so interaction with the opening
window is impossible.

https://bugzilla.gnome.org/show_bug.cgi?id=659164
2011-09-16 09:45:11 -04:00
36d564526c build: Fix rule to create .service files
It was always taking the first .service.in file to create all .service
files: org.gnome.Shell.CalendarServer.service.in was the only one being
used.

https://bugzilla.gnome.org/show_bug.cgi?id=659194
2011-09-16 09:21:14 +02:00
063f34b5d3 browser-plugin: Add support for BROWSER_PLUGIN_DIR environment variable
This makes it easy to override where the plugin should be installed.

This is based on what totem does.

https://bugzilla.gnome.org/show_bug.cgi?id=659123
2011-09-16 00:17:59 +02:00
9486ca5975 Updated Slovenian translation 2011-09-15 22:44:09 +02:00
63ead272a9 Updated Slovenian translation 2011-09-15 22:37:02 +02:00
9dfa2ad84e app-display: Remove AppIconMenu::popup
The signal is just duplicating the parent class' ::open-state-changed
signal, so remove it.
2011-09-15 22:01:58 +02:00
523e431ece tests: Add transition test
https://bugzilla.gnome.org/show_bug.cgi?id=658092
2011-09-15 13:27:12 -04:00
768c4a0dd5 Updated Punjabi Translation 2011-09-15 20:49:58 +05:30
fd25cf30ff messageTray: fix a warning
Check for click in keyboardBox rather than Main.keyboard.actor, since
the latter won't exist when the keyboard is hidden.

https://bugzilla.gnome.org/show_bug.cgi?id=658598
2011-09-15 08:34:29 -04:00
1d14488a4f telepathyClient: notify only once per account for connection error
https://bugzilla.gnome.org/show_bug.cgi?id=659050
2011-09-15 10:10:22 +02:00
6e32c97c43 Updated Bulgarian translation 2011-09-15 07:16:02 +03:00
020fe35d98 Bump version to 3.1.91.1
Update NEW
2011-09-14 18:55:52 -04:00
e0cf946611 Switch to dist-xz
'xz' is now the default tarball format for GNOME, so build
our tarballs that way.
2011-09-14 18:55:52 -04:00
a42ae6fe3a tests/Makefile.am: add missing file to DISTCHECK 2011-09-14 18:55:52 -04:00
196ab6a7c3 Updated Galician translations 2011-09-14 23:08:17 +02:00
f3235882f3 Updated Spanish translation 2011-09-14 21:34:46 +02:00
0518d69b72 panel: Fix corner highlight
The style of the top bar's corners is bound to the style of the
corresponding button; we used to hardcode this association, but
as the login mode does have a different layout, the button is now
determined programmatically.
Unfortunately, some containers take the text direction into account
when ordering their children, while some don't, so the current
code returned the wrong button in RTL locales.

https://bugzilla.gnome.org/show_bug.cgi?id=658983
2011-09-14 21:26:58 +02:00
c9ba94d4d8 Updated Belarusian translation. 2011-09-14 22:03:24 +03:00
50e4a40bb2 Updated French translation
Contributed by Luc Guillemin, Luc Pionchon, Bruno Brouard and Claude
Paroz
2011-09-14 18:35:53 +02:00
351421c158 updated Tamil translation 2011-09-14 21:16:19 +05:30
c3ac2df9ca Updated Polish translation 2011-09-14 16:45:06 +02:00
7c4f632e06 Updated Brazilian Portuguese translation. Reviewed by Henrique P. Machado <hpmachado@gnome.org> and myself. 2011-09-14 11:17:31 -03:00
a4eb3c17eb end-session-dialog: Use correct plural forms for timeouts
All end-session dialogs need to use ngettext for their timeout
strings, fix this.

https://bugzilla.gnome.org/show_bug.cgi?id=639987
2011-09-14 15:43:22 +02:00
e81cee3949 network-status: Remove colloquialism in string
s/You're/You are/

https://bugzilla.gnome.org/show_bug.cgi?id=645037
2011-09-14 15:43:22 +02:00
e943dcafa4 power-status: Mark number with percentage for translation
Some locales expect the percentage sign before the number while
others use a different character, so mark it for translation.

https://bugzilla.gnome.org/show_bug.cgi?id=644097
2011-09-14 15:43:21 +02:00
a324c390f0 Updated Spanish translation 2011-09-14 11:57:09 +02:00
1cf64b5471 workspace: Fix activating windows from xdnd operations
Commit e5bc3a2ba8 changed the hierarchy of WindowClone, which
broke activating windows on hover during xdnd operations. To
avoid intrusive changes, just hide the new actor from picks so
that DND operations pick the actor actually meant to represent
the corresponding window.

https://bugzilla.gnome.org/show_bug.cgi?id=658640
2011-09-14 10:00:46 +02:00
234a4f3a1b hindi update 2011-09-14 10:30:21 +05:30
e69d561e8e updated Tamil translation 2011-09-14 05:41:44 +05:30
b8f2abcbfa Updated Polish translation 2011-09-14 00:47:39 +02:00
189617dfb2 Updated POTFILES.in 2011-09-14 00:43:08 +02:00
c4bfb1e400 l10n: Updated Italian translation 2011-09-14 00:09:48 +02:00
7db92ad5d9 extensionSystem: Add an explicit approval dialog
Pop up a dialog when trying to install an extension so that users are aware
they are installing one. This is a security precaution in the case that an XSS
exploit has been found on the website, which could cause someone to inject a
<script> tag and silently install an extension.

https://bugzilla.gnome.org/show_bug.cgi?id=658612
2011-09-13 12:34:49 -04:00
fa593a3e15 extensionSystem: Use the system cert list
libsoup won't check for a valid cert by default, so copy some logic from
glib-networking to check against the system cert list. Additionally, allow a
fallback for developers, ~/.local/share/extensions.gnome.org.crt, for easy
local development of the website.

https://bugzilla.gnome.org/show_bug.cgi?id=658870
2011-09-13 12:34:48 -04:00
8424236daa theme/gdm: Set a width to the prompt entry
Otherwise it'll keep growing.

https://bugzilla.gnome.org/show_bug.cgi?id=658921
2011-09-13 12:18:45 -04:00
5c14be28f3 updated Tamil translation 2011-09-13 17:39:29 +05:30
9ef80f6ac6 update Simplified Chinese (zh_CN) translation 2011-09-13 11:59:04 +08:00
daf81b5cae Updated Korean translation 2011-09-13 12:06:53 +09:00
7b407dda91 browser-plugin: some buildsys fixes
* fix installation folder

* include headers files

* add Makefile to AC_CONFIG_FILES
2011-09-12 16:23:40 -04:00
4c50293f06 Updated Norwegian bokmål translation 2011-09-12 20:53:27 +02:00
02b8804b96 extensionSystem: Always enable an extension for a user
When the user installs an extension, we always enable it. Change the
'enabled-extensions' key, if necessary, to reflect this.

https://bugzilla.gnome.org/show_bug.cgi?id=658612
2011-09-12 14:37:20 -04:00
7928f90cf6 extensionSystem: Add "UninstallExtension" DBus method
For those who like their system pure, this provides the ability to purge a
pesky extension and its precious place on your disk space, and in your
"Local Extension" list.

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

Conflicts:

	js/ui/extensionSystem.js
2011-09-12 14:37:20 -04:00
d5e6ea6ebd extensionSystem: Replace manifest system with a more direct approach
For security reasons, we shouldn't allow the Shell to download and install
any extension URL.

https://bugzilla.gnome.org/show_bug.cgi?id=658612
2011-09-12 14:37:20 -04:00
52273b6c03 Move sweettooth-plugin into gnome-shell tree, rebrand
This is a direct import from http://github.com/magcius/sweettooth-plugin .
All the source code is the same, as it had no branding. Everything else is
just renaming and modifying the "build system" to work with gnome-shell's
existing one.

https://bugzilla.gnome.org/show_bug.cgi?id=658070
2011-09-12 14:37:20 -04:00
b3d663b758 Updated Swedish translation 2011-09-12 20:27:17 +02:00
c0028694d4 Updated Latvian translation. 2011-09-11 21:40:52 +03:00
3b0110849c Updated British English translation 2011-09-11 15:02:51 +01:00
7b1deba590 Network: destroy notifications when the connection completes
When you finish reconnecting, we should remove any Connection Lost
or Connection Failed notification.

https://bugzilla.gnome.org/show_bug.cgi?id=658049
2011-09-11 12:36:16 +02:00
7a91f318f0 Updated Portuguese translation 2011-09-11 10:46:18 +01:00
2dfde89008 Updated Indonesian translation 2011-09-11 16:03:45 +07:00
23146c00ba Updated Spanish translation 2011-09-11 10:44:36 +02:00
cdd95fbbdc update PunjabiTranslation 2011-09-10 15:12:37 +05:30
01064af20e Updated Spanish translation 2011-09-10 11:12:45 +02:00
01a9c3fc8e Updated Spanish translation 2011-09-10 10:52:26 +02:00
c1734b279b Updated Traditional Chinese translation(Hong Kong and Taiwan) 2011-09-10 15:02:02 +08:00
b00dc31b55 Updated galician translations 2011-09-10 03:37:32 +02:00
c5f8c8c77b Updated Polish translation 2011-09-09 22:55:08 +02:00
9361c7223d Add context to ambiguous strings in login dialog
Fixes https://bugzilla.gnome.org/show_bug.cgi?id=658414
2011-09-09 22:46:23 +02:00
b43cc22704 Make maintainer mode enabled by default
See http://blogs.gnome.org/desrt/2011/09/08/am_maintainer_mode-is-not-cool/
2011-09-09 16:14:58 +01:00
8af4fd64c1 telepathyClient: Fix undefined variable
https://bugzilla.gnome.org/show_bug.cgi?id=658624
2011-09-09 10:50:11 -04:00
5b39b87199 Fix up a11y settings handling
Somebody changed the type of the atk-bridge-location key
from s to ay. Without telling affected parties :-(

https://bugzilla.gnome.org/show_bug.cgi?id=658562
2011-09-08 18:38:29 -04:00
092338a4c7 keyboard: hide the keyboard when unfocusing a shell entry
Previously we explicitly didn't do this, although I can't figure out
why now... definitely seems to work more smoothly this way.

https://bugzilla.gnome.org/show_bug.cgi?id=658591
2011-09-08 15:51:22 -04:00
7907b19e28 l10n: Updated Italian translation 2011-09-08 21:36:34 +02:00
5790309e88 Updated Polish translation 2011-09-08 20:57:25 +02:00
9496b64c86 messageTray: hide the summary box pointer if all its notifications are destroyed
This ensures that we don't show an animation of an empty blob being hidden when
clicking an action button causes the notification to be destroyed.

https://bugzilla.gnome.org/show_bug.cgi?id=658525
2011-09-08 14:27:39 -04:00
f7fb6b2160 messageTray: store notifications and signal ids in an array of objects in SummaryItem
The code is cleaner that way. It also allows us to find the notification object
after the notification actor is destroyed and disconnect the signals.
2011-09-08 14:27:39 -04:00
98649f9397 apps: handle 'app-state-changed' move in shell-app-usage
commit 0af108211c moved which
object the app-state-changed signal is emitted from.

This commit updates shell-app-usage.c to keep up with the times.

https://bugzilla.gnome.org/show_bug.cgi?id=658503
2011-09-08 10:20:06 -04:00
45a1ceaa54 gdm: hide session list if only one session
The session list is supposed to hide itself if

1) the user is already logged in
2) there is only one xsession file installed

There was a bug causing 2) not to work.
This commit fixes that bug.

https://bugzilla.gnome.org/show_bug.cgi?id=658423
2011-09-08 10:15:25 -04:00
6496205081 tests: add a unit test for Misc.findUrls()
https://bugzilla.gnome.org/show_bug.cgi?id=636252
2011-09-08 09:09:12 -04:00
e2898bea5c findUrl: be pickier about what can precede a URL
findUrl() was seeing strings like "You have 1 new message in
foo@example.com/Inbox" and finding the URL
"[http://]example.com/Inbox". Require that URLs either start at the
start of the string, or are preceded by whitespace or an open
paren/quote/etc.

(Since JS doesn't have look-behind assertions like perl does, we have
to actually match the URL-preceding character in the regex, and then
adjust the result findUrl returns accordingly.)

https://bugzilla.gnome.org/show_bug.cgi?id=636252
2011-09-08 09:07:03 -04:00
563221698c findUrl: don't match non-:// URLs
Although "x:5" could theoretically be a URL with scheme "x" and path
"5", it probably isn't. Only URLify strings that use the "authority"
syntax ("foo://"). (No one ever types out "mailto:" URLs in ordinary
text, so we don't want to match those either.)

https://bugzilla.gnome.org/show_bug.cgi?id=636252
2011-09-08 09:06:39 -04:00
92a85071bc findUrl: document the URL-matching regex
Explode the regex onto multiple lines, and add comments explaining the
pieces. Also, change ()s to (?:)s (non-capturing groups) where
appropriate, and replace the UTF-8 characters with \u escapes so that
they actually work.

https://bugzilla.gnome.org/show_bug.cgi?id=636252
2011-09-08 09:02:15 -04:00
9ae8d90be4 ShellStack: make this an StWidget
Base ShellStack on StContainer rather than ClutterGroup, so that it
has StWidget-y features (and so we don't have to "cheat" in
shell_stack_allocate()). Implement navigate_focus() to only ever pass
focus into the top-most child, since doing otherwise would be
surprising.

https://bugzilla.gnome.org/show_bug.cgi?id=646934
2011-09-08 08:49:55 -04:00
caade78e79 popupMenu: do height-for-width negotiation
https://bugzilla.gnome.org/show_bug.cgi?id=652837
2011-09-08 08:47:10 -04:00
4f22c61b16 Update Finnish translation. 2011-09-08 14:52:42 +03:00
5e220e9e47 Updated galician translations 2011-09-08 00:15:22 +02:00
857cb9c501 shell-util: Hide week_start string from xgettext
The translation is taken from GTK+, so translations in the gnome-shell
domain are pointless - hide the string, so that it is not extracted
by xgettext.
2011-09-07 23:55:01 +02:00
09fa5d98c2 network-agent: Add missing parameter
_getMobileSecrets() expects the connection type to be passed, so
do that.

https://bugzilla.gnome.org/show_bug.cgi?id=658492
2011-09-07 22:23:53 +02:00
268fe464de Updated Slovenian translation 2011-09-07 20:51:01 +02:00
27095d1a02 Added Oriya language into the list. 2011-09-07 18:54:09 +05:30
dd061fed38 Updated Oriya Translation 2011-09-07 18:52:05 +05:30
7ddaa8968a Bump version to 3.1.91
Update NEWS
2011-09-06 21:59:21 -04:00
cec1557d04 Updated Belarusian translation. 2011-09-07 01:54:05 +03:00
84a49a5eef Updated Persian translation 2011-09-07 01:34:50 +04:30
06ea16bdbf Updated Norwegian bokmål translation 2011-09-06 22:11:17 +02:00
361d38f93b jhbuild moduleset: use glib branch glib-2-30 2011-09-06 15:49:33 -04:00
016e384fa1 lookingGlass: fix to not overlap the keyboard
When the keyboard is configured, make lg shorter (if necessary) to
avoid overlapping it.

Also, make a few simplifications to lg's layout code. In particular,
move it into panelBox, to simplify its interactions with the panel.

https://bugzilla.gnome.org/show_bug.cgi?id=657986
2011-09-06 14:33:02 -04:00
ef57c9ff83 layout: fix keyboard in modal dialogs
Fix the keyboard in modal dialogs (such as the run dialog) by raising
it above the ModalDialog lightbox so you can use it to type.

https://bugzilla.gnome.org/show_bug.cgi?id=657986
2011-09-06 14:33:02 -04:00
627fff967f layout: separate keyboard and tray
The keyboard and tray need to animate together, but they sometimes
need to be in different stacking layers (eg, from the screensaver you
want access to the keyboard, but not the tray). So remove _bottomBox
and just keep trayBox and keyboardBox lined up manually.

https://bugzilla.gnome.org/show_bug.cgi?id=657986
2011-09-06 14:33:02 -04:00
e79c093d80 layout: add chrome actors directly to uiGroup
Rather than having a single chrome layer and putting all of the chrome
into that, put the chrome actors directly into uiGroup, so that they
can be stacked independently of one another relative to other actors.

(This requires making uiGroup a ShellGenericContainer, so we can use
skip_paint to avoid painting non-visibleInFullscreen chrome when we're
in fullscreen.)

https://bugzilla.gnome.org/show_bug.cgi?id=657986
2011-09-06 14:33:02 -04:00
cbb3831c7b shell-generic-container: document signals
In particular, document that you can ignore the get-preferred-*
signals if and only if you have a fixed width/height specified by some
other means.

https://bugzilla.gnome.org/show_bug.cgi?id=657986
2011-09-06 14:33:02 -04:00
9752fda1f6 keyboard: create and destroy this.actor
Rather than having Main.keyboard.actor always exist, and creating and
destroying only its contents, create and destroy that actor as well.

https://bugzilla.gnome.org/show_bug.cgi?id=657986
2011-09-06 14:33:02 -04:00
d99f08b9c4 layout: fix a few keyboard show/hide bugs
https://bugzilla.gnome.org/show_bug.cgi?id=657986
2011-09-06 14:33:02 -04:00
bf0bcaa306 layout: fix initial struts
The struts were being set while the panel was offscreen (starting its
slide-in animation), and then belatedly getting fixed the next time
something else caused a chrome update. Fix this by setting them before
the animation, and freezing them during the animation.

https://bugzilla.gnome.org/show_bug.cgi?id=657986
2011-09-06 14:33:02 -04:00
a199c1290b main: add back layoutManager.init() call that was accidentally removed
Fixes the chrome to update when entering/leaving the overview again

https://bugzilla.gnome.org/show_bug.cgi?id=657986
2011-09-06 14:33:02 -04:00
28bb0c1fb2 altTab: make thumbnails scroll to right if needed at first Alt+Shift+Above_Tab
Force an allocation at thumbnails creation so we can figure out whether we
need to scroll when selecting.

We also need to show() the whole AltTabPopup before calling _select() so that,
when computing the scrolling offset, the widgets already have their styles
loaded. Otherwise we will miss the switcher list item container's spacing.

https://bugzilla.gnome.org/show_bug.cgi?id=655069
2011-09-06 19:28:51 +01:00
49259b3d19 Updated Spanish translation 2011-09-06 20:04:43 +02:00
a5d0ac7955 user-menu: Rename "Do Not Disturb" to "Notifications"
At least for the foreseeable future, the gnome-session desktop
presence won't be used for anything but suppressing (non-urgent)
notifications. To clarify this behavior, rename the "Do Not Disturb"
switch to "Notifications" (and adjust the switch logic accordingly).

https://bugzilla.gnome.org/show_bug.cgi?id=652718
2011-09-06 17:19:08 +02:00
f326dde09c Updated Japanese translation 2011-09-06 16:32:51 +09:00
3b6d907577 appDisplay: Do not show NoDisplay directories either
https://bugzilla.gnome.org/show_bug.cgi?id=658176
2011-09-05 22:16:02 -04:00
0af108211c apps: Ensure running apps override new .desktop file data
This patch fixes the "apps vanish from alt-TAB bug".

If a "package system" rips away and possibly replaces .desktop files
at some random time, we have historically used inotify to detect this
and reread state (in a racy way, but...).  In GNOME 2, this was
generally not too problematic because the menu widget was totally
separate from the list of windows - and the data they operate on was
disjoint as well.

In GNOME 3 we unify these, and this creates architectural problems
because the windows are tied to the app.

What this patch tries to do is, when rereading the application state,
if we have a running application, we keep that app around instead of
making a new instance.  This ensures we preserve any state such as the
set of open windows.

This requires moving the running state into ShellAppSystem.  Adjust
callers as necessary, and while we're at it drop the unused "contexts"
stuff.

This is just a somewhat quick band-aid; a REAL fix would require us
having low-level control over application installation.  As long as
we're on top of random broken tar+wget wrappers, it will be gross.

A slight future improvement to this patch would add an explicit
"merge" between the old and new data.  I think probably we always keep
around the ShellApp corresponding to a given ID, but replace its
GMenuTreeEntry.

https://bugzilla.gnome.org/show_bug.cgi?id=657990
2011-09-05 17:29:41 -04:00
902e78d910 Updated Danish translation 2011-09-05 23:12:52 +02:00
885e194c25 run-js-test: fix
Needs to explicitly initialize the ShellGlobal now

https://bugzilla.gnome.org/show_bug.cgi?id=649631
2011-09-05 12:23:59 -04:00
d99a2f19a5 Revert "volume: Increase maximum by 50% for outputs with decibel support"
This reverts commit bdd805a3ee.

Conflicts:

	js/ui/status/volume.js

https://bugzilla.gnome.org/show_bug.cgi?id=657607
2011-09-05 11:48:37 +01:00
152a7fd1ef Updated galician translations 2011-09-04 20:01:33 +02:00
6ceb6545ee Updated Belarusian translation. 2011-09-04 19:01:55 +03:00
a167b16860 update PunjabiTranslation 2011-09-04 19:09:33 +05:30
595be5083c overview: Add API to remove search providers
As extensions are now expected to provide a "disable" function,
they need to remove search providers they added. Implement the
removal functionality and add a public removeSearchProvider()
method.

https://bugzilla.gnome.org/show_bug.cgi?id=657548
2011-09-04 13:28:08 +02:00
8b796e745d overview: Add public API to add search providers
Add a addSearchProvider() method, so extensions don't have to access the
view selector directly, which is now a private property of the overview.

https://bugzilla.gnome.org/show_bug.cgi?id=658113
2011-09-04 13:28:08 +02:00
d5314736de updated Tamil translation 2011-09-04 12:00:52 +05:30
a517ab964e Updated Tamil translation 2011-09-04 12:00:52 +05:30
b6c0a2c197 Updated Lithuanian translation 2011-09-04 00:10:59 +03:00
0151c92a92 Added Norwegian bokmål translation 2011-09-03 13:58:46 +02:00
a39440fff5 Updated Traditional Chinese translation(Hong Kong and Taiwan) 2011-09-03 19:17:06 +08:00
5af7e9f6fe Updated Russian translation 2011-09-03 10:52:34 +04:00
b8d9273a7c messageTray: Don't try to URL highlight an invisible actor
https://bugzilla.gnome.org/show_bug.cgi?id=656142
2011-09-02 17:23:14 -04:00
77c4cbb974 Updated Belarusian translation. 2011-09-02 21:39:24 +03:00
8adc193d73 user-menu: Keep track of current presence
We keep track of presence changes by connecting to the
Tp.AccountManager:most-available-presence-changed signal.
However, if multiple accounts are in use, telepathy may
lie to us and emit the signal even when the most available
presence is unchanged.
Work around this by keeping track of the current presence
ourselves.

https://bugzilla.gnome.org/show_bug.cgi?id=657703
2011-09-02 18:13:09 +02:00
86aa4fe0a9 popup-menu: Allow updating combobox items with scroll wheel
Rather than opening the combobox menu, allow using the scroll wheel
to cycle through the visible items.

https://bugzilla.gnome.org/show_bug.cgi?id=657973
2011-09-02 18:08:54 +02:00
8c9eb6702d Switch default browser to Epiphany
1) This is the official GNOME browser.  The default favorite apps
   are just some random picks, and anyone who wants to
   switch can obviously use patches.
2) mozilla.desktop isn't actually even upstream in Firefox, so this
   devolves to patches Fedora/Ubuntu carry to make one, meaning
   that others have to patch the app list anyways.

https://bugzilla.gnome.org/show_bug.cgi?id=650616
2011-09-02 11:55:44 -04:00
b245ee6212 Fix bus name in error message
Signed-off-by: Marc-Antoine Perennou <Marc-Antoine@Perennou.com>
2011-09-02 11:54:56 -04:00
98fa71ba18 main: grab the keyboard D-Bus interface at startup
https://bugzilla.gnome.org/show_bug.cgi?id=658065
2011-09-02 11:41:02 -04:00
22dcb46bff [l10n] Updated German translation 2011-09-02 16:55:32 +02:00
3aa904da0a workspacesView: Only connect to nWorkspacesChanged after we've lazily inited
We lazily init this._workspaces, so we shouldn't try to run any code that uses
it until after it's been created.

https://bugzilla.gnome.org/show_bug.cgi?id=658007
2011-09-01 16:25:03 -04:00
794f773620 Updated Spanish translation 2011-09-01 19:41:53 +02:00
bbba77c275 Updated Swedish translation 2011-09-01 16:07:09 +02:00
88f2bbba61 telepathyClient: don't try to translate concatenations of strings
xgettext doesn't recognize it

https://bugzilla.gnome.org/show_bug.cgi?id=657759
2011-09-01 09:20:11 -04:00
cc8d4e4ab3 Added Norwegian bokmål translation 2011-09-01 08:07:11 +02:00
32a11bd8c7 Added Norwegian bokmål translation 2011-09-01 08:07:11 +02:00
55c82a389c Updated galician translations 2011-08-31 22:27:58 +02:00
a1aa58bb64 Bump version to 3.1.90.1
Update NEWS
2011-08-31 11:48:43 -04:00
82ffe233dc Updated Spanish translation 2011-08-30 21:36:47 +02:00
9e16bb85e3 autorun: use the new "media-removable" icon
gnome-icon-theme recently added this icon for removable devices.

https://bugzilla.gnome.org/show_bug.cgi?id=657757
2011-08-30 15:30:10 -04:00
887b41bce3 Updated Spanish translation 2011-08-30 21:24:52 +02:00
63eef286c9 Fixed and updated Russian translation 2011-08-30 20:32:22 +04:00
14e8cba2b1 Add some (element-type) annotations to appease g-i master 2011-08-30 12:07:43 -04:00
3418e6e85e Add support for asynchronous search providers
Some search providers may want to change their results, or may not
want to block on an external service to get their results (DBus, etc.)
Set up an infrastructure to allow search providers to add their search
results at a later time.

Based on a patch by Jasper St. Pierre and Seif Lotfy.

https://bugzilla.gnome.org/show_bug.cgi?id=655220
2011-08-30 11:55:28 -04:00
8f0c980d3c Updated POTFILES.in 2011-08-30 17:39:09 +02:00
c86f3b8d48 build: Make caribou depend on libgee
https://bugzilla.gnome.org/show_bug.cgi?id=657697
2011-08-30 15:14:22 +02:00
8cf6b4c728 don't translate IM status
They are well-known strings defined in the Telepathy spec and so shouldn't be
translated.

https://bugzilla.gnome.org/show_bug.cgi?id=657696
2011-08-30 14:36:02 +02:00
612b9e9faf Fix batch import for loginDialog
Signed-off-by: Marc-Antoine Perennou <Marc-Antoine@Perennou.com>
Signed-off-by: Adel Gadllah <adel.gadllah@gmail.com>
2011-08-30 13:58:02 +02:00
c2c4c26f72 Don't crash displaying contacts with no alias
If a folks individual has no alias we crash when passing in NULL
to strstr(). Fix this by checking for non-null first.
2011-08-30 11:13:35 +02:00
be4d504e27 userMenu: Don't show the default avatar over a newly selected one
When we replace the default avatar image with a real image, we need
to remove the default avatar image.

https://bugzilla.gnome.org/show_bug.cgi?id=657657
2011-08-29 20:12:16 -04:00
9ed0bbb3a9 Bump version to 3.1.90
Update NEWS
2011-08-29 19:24:14 -04:00
e5bc3a2ba8 overview: Compensate for the window's invisible borders
https://bugzilla.gnome.org/show_bug.cgi?id=656335
2011-08-29 19:16:52 -04:00
1dee10c575 Toggle mutter's unredirect features on/off depending on the situation
We disable it when in the overview or when recording a video and otherwise
leave it enabled.

https://bugzilla.gnome.org/show_bug.cgi?id=618497
2011-08-30 00:15:52 +02:00
352fb7b833 search: Allow searching for people in overview mode
This adds contacts search to shell, powered by libfolks.
Changes:

- Add Folks and Gee to the build system
- ShellContactSystem, a backend in C
- ContactDisplay, search frontend in JS

https://bugzilla.gnome.org/show_bug.cgi?id=643018
2011-08-29 17:43:30 -04:00
81cee34c17 Make GridSearchResults take a grid as an optional parameter
This is useful since contact search results use a custom grid

https://bugzilla.gnome.org/show_bug.cgi?id=643018
2011-08-29 17:29:11 -04:00
fa786fd3ef Replace GdmUser with AccountsService
The GdmUser copy+paste code has been superseded by AccountsService,
so kill the former and use the latter.

https://bugzilla.gnome.org/show_bug.cgi?id=650893
2011-08-29 22:53:41 +02:00
0751a90bd9 user-menu: Implement new mockups
The current user status menu allow to set the session status,
which also influences the IM status when signed in with
mission-control. However, the way it is presented to the user
makes it hard to figure out how the statuses interact or that
there are two distinct status in the first place.
Therefore, use a separate control for each status, and update the
overall look to match gnome-contacts.

https://bugzilla.gnome.org/show_bug.cgi?id=652837
2011-08-29 22:11:14 +02:00
709193d680 popup-menu: Expand switch menu items
Given that our menus contain at most two columns, all switch widgets
in menus end up in the last columns, and thus aligned with the right
menu edge.
However, the updated user status menu will contain a section which
ignores the menu's column layout, so the switch might end up in
the middle of the menu if the overall width is determined by said
section.
At least for now, we always want the switch to align with the end,
so just expand switch menu items rather than adding an option.

https://bugzilla.gnome.org/show_bug.cgi?id=652837
2011-08-29 22:11:14 +02:00
d0d82cdf7e popup-menu: Add combo box menu item
Introduce a new menu widget, which displays the active item from
a set of options, and pops up a child menu to allow changing the
active item when activated.

https://bugzilla.gnome.org/show_bug.cgi?id=652837
2011-08-29 22:11:14 +02:00
5d2b7e2c9e popup-menu: Add support for child menus
Allow opening a popup menu from another menu. While the child menu
is open, events on the parent menu are blocked. The parent menu
is kept open when the child menu is closed; the child menu on the
other hand is closed with the parent, e.g. when the focus moves
to another toplevel menu.
This feature will be used to implement combo box menu items.

https://bugzilla.gnome.org/show_bug.cgi?id=652837
2011-08-29 22:11:14 +02:00
07660f7fcf status-menu: Rename to userMenu
We haven't actually been calling the top-right menu "status menu" for
quite some time, so use the upcoming code changes as an excuse for
renaming it to "user menu".
2011-08-29 22:11:09 +02:00
5d138e1b79 appDisplay: Don't show NoDisplay items in the Applications tab 2011-08-29 15:56:07 -04:00
0133c6e174 Favorites: Add gnome-documents to default list
We currenty don't have "finding and reminding" so we add gnome-documents
to the default favorites list to improve the document handling user expirence.

https://bugzilla.gnome.org/show_bug.cgi?id=657520
2011-08-29 21:52:46 +02:00
2054f77e2b Add 'multi-line-notification' class name if the notification image is set
Notifications with images are multi line notification.
2011-08-29 15:21:22 -04:00
e4911e2f7a Specify the style for the 'multi-line-notification' correctly
It was not taking effect before.
2011-08-29 15:21:15 -04:00
eabc2e6781 Updated POTFILES.in 2011-08-29 20:46:20 +02:00
71cf87cbb1 Updated POTFILES.in 2011-08-29 20:21:25 +02:00
155997b5fa Overview: dim the background with the dim-factor property
Doing this rather than overdrawing a black rectangle saves us
(pixels in screen) * 8 bytes of memory bandwidth for every frame we draw going
into the overview.

It also allows us to dim the background on non-primary monitors making the
overall overview appearance consistent across all monitors.

https://bugzilla.gnome.org/show_bug.cgi?id=656433
2011-08-29 19:17:01 +01:00
82ce8fe3ff Use Meta.BackgroundActor instances instead of cloning global.background_actor
Instances of this class share a single CoglTexture behind the scenes which
allows us to show the background with different rendering options without
duplicating the texture data.

https://bugzilla.gnome.org/show_bug.cgi?id=656433
2011-08-29 19:17:01 +01:00
9f1da20161 Add support for gdm greeter session
This commit adds GDM session support.

It provides a user list that talks to GDM,
handles authentication via PAM, etc.

It doesn't currently support fingerprint readers
and smartcards.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-29 14:11:36 -04:00
d4239d570d main: Factor out remaining user session specific bits
The shell has a number of things that are only relevant for
logged in users (e.g. calendar events, telepathy integration, a
user menu, etc).

This commit moves those user session specific bits into their
own functions in preparation for making the shell code ready
for use at login time.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-29 14:11:35 -04:00
1ecbabc69a panel: Dynamically match corner to style of nearest button
Right now the panel code makes the left corner sync up with the
activities button and the right corner sync up with the user menu.
This is fine as long as we have an activities button and a user menu.

The login screen won't have those things, though.

This commit changes the panel corner code to try to figure out which
interface element is the most appropriate to sync up with based on
its position in the panel.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-29 14:11:35 -04:00
7f767c49d8 popupMenu: Raise menu when popping it up
When a menu gets popped up, it should never
pop behind anything else in the shell.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-29 14:11:35 -04:00
aabe56ba79 messageTray: implement showing images in notifications
Images are part of the notification spec, so we should support them.

Marina Zhurakhinskaya provided some code for getting the layout right
for this patch.

https://bugzilla.gnome.org/show_bug.cgi?id=621009
2011-08-29 13:46:47 -04:00
d227ddfc88 keyboard: add an on-screen keyboard
https://bugzilla.gnome.org/show_bug.cgi?id=612662
2011-08-29 12:59:25 -04:00
021d3dadbb layout: add panelBox and trayBox
Have LayoutManager automatically deal with sizing and positioning
boxes for the panel and messageTray relative to the monitors.

Also, now that LayoutManager knows exactly where and how tall the
panel and tray are, have it manage the pointer barriers as well.

https://bugzilla.gnome.org/show_bug.cgi?id=612662
2011-08-29 12:59:25 -04:00
4902a600d5 batch: Add mechanism for doing animation series
In order for transformation animations to look good, they need to be
incremental and have some order to them (e.g., fade out hidden items,
then shrink to close the void left over).

Chaining animations in this way can be error prone and wordy using just
Tweener callbacks.

This commit adds a new set of classes to help:

 - Task.  encapsulates schedulable work to be run in a specific scope.

 - ConsecutiveBatch.  runs a series of tasks in order and completes
                      when the last in the series finishes.

 - ConcurrentBatch.  runs a set of tasks at the same time and completes
                     when the last to finish completes.

 - Hold.  prevents a batch from completing the pending task until
          the hold is released.

The tasks associated with a batch are specified in a list at batch
construction time as either task objects or plain functions.
Batches are task objects, themselves, so they can be nested.

For now, these APIs are temporarily getting staged in a gdm/ specific
subdirectory so they will be available for use by GDM.  They aren't
specific to GDM, or even to doing animations, though, so the API may eventually
move in some form or another to a more general location. Alternatively, the
APIs may ultimately get dropped entirely and replaced by something else.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:44:09 -04:00
db39ba3b9f modalDialog: add mode that leaves shell reactive
A modal dialog in the shell blocks anything but that dialog from
receiving user input. Applications within the session and other
parts of UI are rendered non-reactive.

When GDM gets changed to use the shell for its greeter, the user
list will be presented as a shell dialog. That dialog shouldn't
block access to the panel menus, etc.

This commit adds a shellReactive property that makes the ModalDialog
class continue to block access to applications, but allow the user
to interact with the shell itself.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:36:57 -04:00
239a9e4816 popupMenu: Hide settings menus outside user session
The control-center contains user-pertinent settings
panels. These panels don't make sense to show outside
of a user's session, so hide them for session types other
than SessionType.USER.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:31:34 -04:00
35e99266ba overview: Add dummy mode
We're not going to want an overview at the login screen,
but a lot of code in the shell depends on the overview
existing.

This commit adds a new isDummy constructor property to
allow creating the overview as a non-functional, stub object
that doesn't do anything visible.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:30:52 -04:00
67ae8ed8e9 overview: make shellInfo private
This commit forwards the shellInfo setMessage method
to the overview itself and makes shellInfo private.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:29:41 -04:00
356e4c0967 overview: make dash private
The dash object is currently exposed as a public object.
It's only used outside of the overview for the dash object's
iconSize property though.

This commit makes the dash object private and proxies the dash
iconSize property to the overview.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:25:04 -04:00
80a9d2e7c9 overview: Make viewSelector private
It's only used internally by the overview itself,
and by some performance testing code, so don't
expose it as a public object.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:23:59 -04:00
5088f22388 global: Add concept of "session type"
This commit introduces a "session type" for
gnome-shell.  It essentially defines what
mode of operation the shell runs in
(normal-in-a-users-session mode, or at-the-login-screen mode).

Note this commit only lays the groundwork.  Actually
looking at the key and appropriately differentiating
the UI will happen in subsequent commits.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:22:38 -04:00
4156a4c2d0 shell-global: require init call before shell_global_get()
shell_global_get() currently implicitly instantiates the shell
global singleton the first time it's called.  This means there's
no opportunity to set construction-time properties on the singleton.

This isn't an issue yet, because there aren't any.  We will need it
in the future, though, when we grow a --gdm-mode that gets exposed as
a property through the global singleton.

This commit adds a new _shell_global_init() function that must be
invoked before shell_global_get() can be called.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:20:16 -04:00
b6c2399a17 dateMenu: Make events list optional
Right now, when a user clicks on the panel clock, a menu pops up with a
calendar and a list of events from the user's schedule.  The list of
events only makes sense from within a user's session, however.

As part of the prep work for making the shell a platform for the login
screen, this commit makes the events list optional.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:18:47 -04:00
5be9326192 dateMenu: Force min-width of events area, not whole menu
The theme currently hard codes the minimum size of the calendar
menu to make sure there's a designated area for events
(even if there isn't anything currently scheduled).

A side-effect of the hard coded minimum width is that
if the events area is hidden, the menu ends up much
bigger than the calendar.  We don't currently ever hide
the events area, but we will in the future.

This commit moves the min-width restriction from the menu
specifically to the events area.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:18:13 -04:00
388cfa3695 panelMenu: Separate from ui chrome layer
The chrome layer contains the user interface elements (e.g.,
the panel) that disappear when fullscreen windows get displayed.

Panel menus are currently put in the chrome layer, but don't need
to be, since they are only displayed when the user is interacting
with the shell and not a fullscreen application.

Putting panel menus in the chrome layer does mean they will get
stacked below shell interface elements that aren't in the chrome layer,
though.

This commit changes panel menus to be on the same layer as most other
shell elements, so they get properly stacked above those elements.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:17:13 -04:00
e8914c6699 modalDialog: fade in buttons when first showing them
Right now, if buttons get set on a dialog after it is mapped,
they just pop in instantly.

We shouldn't have any harsh transitions like that, though.

This commit changes the buttons to quickly fade in, instead.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:16:40 -04:00
13bf64a53d popupMenu: Use new convenience method for settings
All the system status menus in the panel offer a
menu item to jump to a relevant part of the
control-center.

This means each status icon has the same, or nearly the
same bit of code to:

- Add a new "action" menu item and listen for its activation.
- Hide the overview if it's showing when the menu item is activated
- Find the relevant control-center panel from its desktop file
- Launch the control-center to the relevant panel

This commit consolidates all those details in a new method,
addSettingsAction.  This refactoring reduces code duplication and
slight inconsistencies in the code resulting from that duplication.
It will also make it easier in subsequent commits to hide settings menu
items when the shell is used in the login screen.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:16:04 -04:00
f96b2ee858 popupMenu: Hide separators when they aren't separating
A separator only makes sense if there are items on both
sides of it. There is quite a lot of code written
throughout the shell that manages the process of showing
and hiding separators as the items around those separators
change.

This commit drops all that code in favor of changes to the menu
implementation to dynamically hide or show separators as
appropriate, so the callers don't have to deal with it.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:15:17 -04:00
b12967b930 st-adjustment: Drop all animation-y stuff
StAdjustment has some non-functional and unused animation vestiges
like the "elastic" property, st_adjustment_interpolate() and
st_adjustment_clamp().

This commit vacuums that stuff up so it doesn't tempt anyone into
trying to use it.

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-28 12:14:17 -04:00
d896248ff8 Complete transitioning away from nm-applet
Wireless and 3g dialog code has moved to gnome-control-center, so
we can stop calling out to nm-applet. Also, we can now enable the
notifications provided by the shell and kill a bit of code about
auth that is not actually needed.

https://bugzilla.gnome.org/show_bug.cgi?id=650244
2011-08-29 18:11:50 +02:00
2ebdc81c8f Add a system modal dialog for network secrets
Using the new ShellNetworkAgent, show a system modal dialog
(similar to the PolicyKit one) when NetworkManager needs secrets
for connecting to wireless.

https://bugzilla.gnome.org/show_bug.cgi?id=650244
2011-08-29 18:11:50 +02:00
2af5e851b3 Add a new network agent for the Shell
A network agent is a component that stores network secrets (like
wifi passwords) in the session keyring. This commit adds an
implementation of it to be used by the shell network dialogs. It
handles most of the keyring stuff, delegating the UI to upper layers.

https://bugzilla.gnome.org/show_bug.cgi?id=650244
2011-08-29 18:11:50 +02:00
bd9455ec8e telepathyClient: Add notification for account connection errors
Based on initial work from Alban Crequy and Xavier Claessens

https://bugzilla.gnome.org/show_bug.cgi?id=654159
2011-08-29 10:35:09 -04:00
fefee3b49e telepathyClient: Add IM subscription request support
Based on initial work from Guillaume Desmottes

https://bugzilla.gnome.org/show_bug.cgi?id=653941
2011-08-29 10:35:09 -04:00
071c49b7c6 [l10n] Updated German translation 2011-08-28 14:04:58 +02:00
4e9e6e75d3 st-box-layout: Document insertion apis
This commit just adds some brief doc comments
to st_box_layout_insert_actor() and
st_box_layout_insert_before()

https://bugzilla.gnome.org/show_bug.cgi?id=657082
2011-08-27 15:25:01 -04:00
a13af7fbcc windowManager: use meta_window_is_attached_dialog()
Use meta_window_is_attached_dialog() so that we only dim/unfold dialog
windows that mutter is actually showing as attached

https://bugzilla.gnome.org/show_bug.cgi?id=646761
2011-08-27 13:14:38 -04:00
4fa8e2b59d extensionSystem: Don't try to make the user extensinons dir if it exists
this removes the "Error invoking Gio.make_directory_with_parents: Error
creating directory: File exists" spam from the Errors tab of the Looking
Glass
2011-08-26 17:34:50 -04:00
90783c7cdf theme: Darken notification entry color a lot
Makes it a lot easier to see what you're typing.
2011-08-26 16:10:13 -04:00
f99b4da4ec KeyboardStatus: disambiguate duplicate short descriptions
If two layouts have the same short description (for example, english
(US) and english (dvorak)), add a subscript for disambiguating
among them.

https://bugzilla.gnome.org/show_bug.cgi?id=650128
2011-08-26 16:52:58 +02:00
a64e0e1f49 VolumeStatus: track PulseAudio state and hide when disconnected
Only show the menu when the associated GvcMixerControl is ready, as
the connection can fail or PulseAudio may not be installed.

https://bugzilla.gnome.org/show_bug.cgi?id=645708
2011-08-25 23:36:22 +02:00
270e82e3db GnomeVolumeControl: track PulseAudio connection state and expose it
Adds get_state() and ::state-changed signals, that replace connecting
and ready, as well as providing indication of when the object was closed
or the connection to PulseAudio failed.

https://bugzilla.gnome.org/show_bug.cgi?id=645708
2011-08-25 23:35:29 +02:00
8f4a4d93f2 panel: Fix ordering of status icons
Otherwise a11y would be added to the right of the power indicator instead of to
the left of keyboard indicator
2011-08-25 17:15:24 -04:00
83265bb12a panel: Start the status area before extensions are loaded
The order of indicators depends on the order of calls to
Panel.addToStatusArea. To have it consistent across enabling and
disabling of extensions, we need to place the core ones first.

https://bugzilla.gnome.org/show_bug.cgi?id=653205
2011-08-25 13:35:49 -04:00
5be8d5f9cf panel: Remove 'display' from the standard icons
This way all standard indicators have a shell implementation
provided, which prevents issues with extensions enabling/disabling
(in particular with xrandr-indicator)

https://bugzilla.gnome.org/show_bug.cgi?id=653205
2011-08-25 13:35:41 -04:00
08126e5a38 panel: Add an easier way of adding items to the system status area
Extensions often want to add items to the system status area, so it
is useful to add a convenience API for it. Also, we now allow
for cleaner destruction of panel objects, by just calling destroy()
on it.
Based on a patch by Jasper St. Pierre.

https://bugzilla.gnome.org/show_bug.cgi?id=653205
2011-08-25 13:34:45 -04:00
b76efe17d6 notificationDaemon: Work around JS interpreter bug
The "id" variable was being sporadically reset to null, and as far as
Florian and I could determine, this is actually a Spidermonkey bug.

The issue has something to do with:

1) use of "let" for the variable
2) Nesting a dynamic closure inside of a for() loop

Work around it here for now - I tried to create a minimized test case
to hand to the Spidermonkey developers, but failed.  A big part of
the problem is it's only sporadically reproducible.
2011-08-25 09:20:00 -04:00
ca2678446d Updated Norwegian bokmål translation 2011-08-24 22:17:14 +02:00
06d906b962 telepathyClient: Add direction containers
Direction containers group all contiguous messages in the same direction into
their own parent container, allowing for smarter styling of similar messages.

https://bugzilla.gnome.org/show_bug.cgi?id=640271
2011-08-24 15:32:15 -04:00
023f5149e5 Updated Galician translations 2011-08-24 21:19:19 +02:00
2e45508529 Updated Spanish translation 2011-08-24 21:10:29 +02:00
6241a8269f extensionSystem: Start using OUT_OF_DATE
We were defining OUT_OF_DATE as a possible state, but never using it
properly.

https://bugzilla.gnome.org/show_bug.cgi?id=654770
2011-08-24 13:58:22 -04:00
465d03ab2c extensionSystem: Add a DOWNLOADING state
https://bugzilla.gnome.org/show_bug.cgi?id=654770
2011-08-24 13:58:22 -04:00
a56cd3c3d6 extensionSystem: Add 'extension-status-changed' signal
https://bugzilla.gnome.org/show_bug.cgi?id=654770
2011-08-24 13:58:22 -04:00
d8a98e5467 extensionSystem: Add install-from-HTTP capability
This adds a new DBus method: InstallExtensionRemote(uuid : s, url : s)

Pass it the UUID of an extension and the URL of a manifest file: the same as a
metadata.json, but with a special key, '__installer', which is an HTTP location
that points to an zip file containing the extension. The Shell will download
and use it to install the extension. In the future, the manifest file may be
used to automatically detect and install updates.

https://bugzilla.gnome.org/show_bug.cgi?id=654770
2011-08-24 13:58:22 -04:00
2d813cbdd8 extensionSystem: Implement new live enable/disable system
The rough sketches of the system are outlined here:

http://mail.gnome.org/archives/gnome-shell-list/2011-June/msg00283.html

Additionally, enable/disable extensions when the 'enabled-extensions' setting
changes. Two new DBus methods, "EnableExtension" and "DisableExtension" allow
users to manipulate this setting quite easily.

https://bugzilla.gnome.org/show_bug.cgi?id=654770
2011-08-24 13:58:22 -04:00
6d3434f3a5 extensionSystem: Remove 'disabled-extensions' blacklist
The two similar keys were hard to manipulate to have specific effects, so just
remove one. Now there is an *explicit* whitelist: all extensions must be in the
'enabled-extensions' for them to be loaded.

https://bugzilla.gnome.org/show_bug.cgi?id=654770
2011-08-24 13:58:22 -04:00
fa0268f35a ShellApp: Avoid crashing during state transition for window-backed apps
During a state transition from running to not-running for
window-backend apps, it's possible we get a request for the icon.
Avoid asserting here and just return an empty image.

https://bugzilla.gnome.org/show_bug.cgi?id=656546
2011-08-24 12:32:33 -04:00
712ea9b9b6 telepathyClient: Use sent timestamp instead of received timestamp
It's generally more useful to see when a person sent a message instead of when
we received it. Also, a recent change in Telepathy made the received timestamp
be 0 for messages we send.

https://bugzilla.gnome.org/show_bug.cgi?id=640271
2011-08-24 11:18:58 -04:00
b7fd78b254 Add screenshot interface
Adds methods to shell_global to allow taking screenshots
save the result into a specified png image.

It exposes three methods via shellDBus applications like
gnome-screenshot:

*) Screenshot (screenshots the whole screen)
*) ScreenshotWindow (screenshots the focused window)
*) ScreenshotArea (screenshots a specific area)

https://bugzilla.gnome.org/show_bug.cgi?id=652952
2011-08-24 16:06:13 +02:00
a2e6b3167b Updated Swedish translation 2011-08-24 09:03:43 +02:00
72037af241 AppDisplay: fix typo that prevented Ctrl+Enter in search
workspace is not a valid variable, the workspace index should be
fetched from the params object.

https://bugzilla.gnome.org/show_bug.cgi?id=657111
2011-08-22 23:28:23 +02:00
62048abbf5 Updated Bulgarian translation 2011-08-22 23:20:37 +03:00
7b61aca956 Updated Persian translation 2011-08-23 00:37:09 +04:30
6709e5e458 NetworkMenu: don't show hidden access points
It is not possible to connect to hidden access points without
knowing the SSID, and it should be done using the control center
panel and the appropriate dialog. At the same time, this should
fix some warnings from libnm-glib and dbus-glib.

https://bugzilla.gnome.org/show_bug.cgi?id=646454
2011-08-22 21:57:29 +02:00
11c8405879 telepathyClient: Delay notification in case it gets acked
The shell should only notify in case no other client handles the message.
Empathy will ack the message if focused, so we don't want to step on its
toes.
2011-08-22 19:18:31 +02:00
6028628261 favorite-apps: Use libreoffice-writer & firefox.desktop
Use firefox.desktop and libreoffice-writer.desktop for favorite-apps
defaults instead of mozilla-firefox and openoffice.org-writer.

https://bugzilla.gnome.org/show_bug.cgi?id=654707
2011-08-22 09:56:30 -04:00
3d8a1537d2 Updated Norwegian bokmål translation 2011-08-21 21:43:36 +02:00
c5b4decdae Updated Norwegian bokmål translation 2011-08-21 21:42:37 +02:00
c7237be3e3 Update Simplified Chinese translation for 3.2 release. 2011-08-21 14:31:57 +00:00
aa39166b74 Updated Indonesian translation, including some strings submitted
by Wibiharto <wibinem@yahoo.com>
2011-08-21 21:28:39 +07:00
e7b9933036 theme: consistent button appearance and behaviour
Update modal dialog, notification and search result buttons to use
the same style. This improves the look of notification buttons
and also ensures that all buttons change appearance when they are
focused or pressed.

Also change all buttons so that their labels are correctly vertically
centered.

https://bugzilla.gnome.org/show_bug.cgi?id=655974
2011-08-19 15:56:55 +01:00
54d51c713c Updated Spanish translation 2011-08-19 12:26:51 +02:00
72e0c2fe11 Updated Hebrew translation. 2011-08-18 23:55:35 +03:00
7458d3ef39 Approve file transfer channels
https://bugzilla.gnome.org/show_bug.cgi?id=653940
2011-08-18 12:35:57 +02:00
52c5f9b144 ApproverSource: takes a gicon rather than an icon name
https://bugzilla.gnome.org/show_bug.cgi?id=653940
2011-08-18 12:35:17 +02:00
8c7085acd4 update translation for Punjabi 2011-08-18 06:58:44 +05:30
083ca7d39b theme - soften notification popups
Some small changes to make notifications fit with the shell aesthetic
and make them match the mockups. Gives them a bigger corner radius,
smaller font and more transparency.

https://bugzilla.gnome.org/show_bug.cgi?id=656732
2011-08-17 13:13:28 +01:00
68e716ae27 theme: new chat input style, smaller meta messages
Match the mockup for chat input style and meta messages - gives
the input box more depth and definition, makes the meta messages
smaller and therefore more distinct and less distracting.

https://bugzilla.gnome.org/show_bug.cgi?id=640271
2011-08-17 12:52:09 +01:00
ef8772916d Revert "network: ignore APs that hide their SSID"
This reverts commit 6a3130e25f.

It would hide those APs forever, even after connecting to them, which users
can do via other means. Thanks to Giovanni Campagna for pointing this.

https://bugzilla.gnome.org/show_bug.cgi?id=654898
2011-08-16 18:39:48 +01:00
b54e374bc5 Updated Galician translations 2011-08-16 19:30:25 +02:00
b3228258ee Updated Lithuanian translation 2011-08-13 22:17:40 +03:00
e5036a458e Updated Traditional Chinese translation(Hong Kong and Taiwan) 2011-08-13 20:19:17 +08:00
c714a66ba3 dateMenu: Watch for a resume, and update the clock
Otherwise it can be very out of date.

https://bugzilla.gnome.org/show_bug.cgi?id=656403
2011-08-12 17:05:57 -04:00
d80b7be6ca Use user-defined calendar application for the date menu calendar button
Use the existing setting

  org.gnome.desktop.default-applications.office.calendar.exec

as calendar application instead of the hard-coded evolution.  Evolution
is still the fallback if that setting is cleared (it defaults to
evolution).

https://bugzilla.gnome.org/show_bug.cgi?id=651190
2011-08-12 12:50:04 -04:00
77de611ec7 dateMenu: Fix some tabs vs spaces 2011-08-12 12:48:32 -04:00
60b54c0052 network: always coerce the SSID to _something_
https://bugzilla.gnome.org/show_bug.cgi?id=654898
2011-08-12 17:26:54 +01:00
6a3130e25f network: ignore APs that hide their SSID
https://bugzilla.gnome.org/show_bug.cgi?id=654898
2011-08-12 17:26:53 +01:00
91cba1f8f4 ShellApp: Make sure that we use a valid timestamp when activating
Otherwise, we'd be comparing against the last_used_time and setting
it to 0.

https://bugzilla.gnome.org/show_bug.cgi?id=656374
2011-08-12 03:21:42 -04:00
22b2661df9 gnome-shell.modules: update to vala 0.13.1
Fixes a crash when compiling the caribou gtk module caused by
a call to async Bus.get_sync() method.
https://bugzilla.gnome.org/show_bug.cgi?id=644275
2011-08-11 13:27:04 -04:00
f8b397a5dc appDisplay: Add missing this
Spotted by Dan Winship <danw@gnome.org>
2011-08-11 10:38:24 -04:00
44b475e746 appDisplay: Fix activation of search results
The id parameter changed to an app.
2011-08-11 10:26:14 -04:00
d25610903a ShellWindowTracker: Rename self variable for consistency
Historically it was monitor, now tracker.

(I want to move more things to self, but that's a different bug).

https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-08-11 10:11:36 -04:00
2efcbaf206 ShellApp: Fix comment about window-backed apps
https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-08-11 10:11:36 -04:00
7f1d2825fd appDisplay: Don't expose "Add as favorite" for window-backed apps
We don't know how to do it.  Similarly, don't allow New Window.

https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-08-11 10:11:22 -04:00
b9edb1dc01 ShellApp: Ensure we set the size of returned texture for window backed apps
Unify the two code paths too.

https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-08-11 05:56:00 -04:00
b0cc778c49 ShellApp: Use stable sequence for id, not pointer address
As danw points out,

  "It's unique during the lifetime of the window, but reasonably likely to be
  reused by another window after this one is destroyed. Using
  meta_window_get_stable_sequence() might be better."

https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-08-11 05:47:38 -04:00
ff840db708 ShellApp: Use global time, not clutter time
This is correct in more circumstances.

https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-08-11 05:45:48 -04:00
11f30e2e09 ShellApp: Use integer for size, not float
We were basically casting it everywhere except for ClutterActor -
let's be consistent with StTextureCache and use integers.

https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-08-11 05:44:19 -04:00
4886275df4 ShellApp: Change activation API
Since almost all of the callers of shell_app_activate were using the
default workspace (by passing -1), remove that parameter.

Add a new shell_app_activate_full() API which takes a workspace as
well as a timestamp; previously we might have been ignoring event
timestamps from elsewhere.

https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-08-11 05:35:23 -04:00
c5de239e25 shell_util_normalize_and_casefold: New utility function
Merge the duplicated copies into shell-util.
2011-08-10 13:00:06 -04:00
10dcc100e9 Kill off ShellAppInfo, move into ShellApp
This dramatically thins down and sanitizes the application code.

The ShellAppSystem changes in a number of ways:
* Preferences are special cased more explicitly; they aren't apps,
  they're shortcuts for an app), and we don't have many of them, so
  don't need e.g. the optimizations in ShellAppSystem for searching.
* get_app() changes to lookup_app() and returns null if an app isn't
  found.  The semantics where it tried to find the .desktop file
  if we didn't know about it were just broken; I am pretty sure no
  caller needs this, and if they do we'll fix them.
* ShellAppSystem maintains two indexes on apps (by desktop file id
  and by GMenuTreeEntry), but is no longer in the business of
  dealing with GMenuTree as far as hierarchy and categories go.  That
  is moved up into js/ui/appDisplay.js.  Actually, it flattens both
  apps and settings.

Also, ShellWindowTracker is now the sole reference-owner for
window-backed apps.  We still do the weird "window:0x1234beef" id
for these apps, but a reference is not stored in ShellAppSystem.

The js/ui/appDisplay.js code is rewritten, and sucks a lot less.
Variable names are clearer:

_apps -> _appIcons
_filterApp -> _visibleApps
_filters -> _categoryBox

Similarly for function names.  We no longer call (for every app) a
recursive lookup in GMenuTree to see if it's in a particular section
on every category switch; it's all cached.

NOTE - this intentionally reverts the incremental loading code from
commit 7813c5b93f.  It's fast enough
here without that.

https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-08-10 12:59:32 -04:00
8ada9b43ae Updated Norwegian bokmål translation 2011-08-10 15:23:32 +02:00
fd77225c3e Updated Spanish translation 2011-08-10 13:27:49 +02:00
7ed3facf8f calendar: Improve week start handling
Add a helper function (mostly copied from gtkcalendar.c) for getting
the first week day for the current locale, using nl_langinfo if
available and falling back to the GTK+ gettext fallback otherwise.

Use that function in the calendar, so that the LC_TIME setting is
used if possible.

https://bugzilla.gnome.org/show_bug.cgi?id=649078
2011-08-10 01:03:26 +02:00
6c97e2a5ab ShellAppUsage: Port to GDBus
(Split out of a larger commit by Colin Walters <walters@verbum.org>)

https://bugzilla.gnome.org/show_bug.cgi?id=648651
2011-08-09 11:46:16 -04:00
f19e8b1e78 ShellGlobal: Remove unused dbus-glib include
https://bugzilla.gnome.org/show_bug.cgi?id=648651
2011-08-09 08:07:34 -04:00
08e669adde layout: Don't create and destroy ripple animations
Instead, create three ripples and keep tweening them. This gives a dramatic
speedup when entering the overview, but means that we can't have the same animation
running twice. In this case, we "reset" the currently running ripple animation, but
it is hard to notice unless looking for it.

https://bugzilla.gnome.org/show_bug.cgi?id=656125
2011-08-08 14:11:09 -04:00
bbd2c02b7f Updated Swedish translation 2011-08-08 08:33:05 +02:00
0d9da86b7e Updated Hebrew translation. 2011-08-06 16:00:28 +03:00
5810fcb14d extensionSystem: Save extension errors per-extension
Extension developers may be confused about why their extensions aren't working:
the LookingGlass isn't a very obvious place, or even which errors are theirs.
To remedy this, save all errors per-UUID which allows them to be retrieved
later.

https://bugzilla.gnome.org/show_bug.cgi?id=654770
2011-08-04 13:40:04 -04:00
2466eb3132 shellDBus: Add a few version parameters
Add ShellVersion, designed for detecting OUT_OF_DATE extensions so they can't
be installed, as well as ApiVersion, designed for backwards-compatibility with
the SweetTooth web-app, which must support all shell versions.

https://bugzilla.gnome.org/show_bug.cgi?id=654770
2011-08-04 13:40:04 -04:00
fc59e222d2 shellDBus: Add ListExtensions() and GetExtensionInfo()
GetExtensionInfo() takes a UUID and returns a JSON object with information
about that extension including their metadata, path and current state.

ListExtensions() takes no arguments and returns a JSON object mapping UUIDs
to the same information objects described above.

https://bugzilla.gnome.org/show_bug.cgi?id=654770
2011-08-04 13:40:04 -04:00
ff983432d9 lookingGlass: Recognize new extensions as they are added
https://bugzilla.gnome.org/show_bug.cgi?id=654770
2011-08-04 13:40:04 -04:00
67b4f9b3a9 workspacesView: belatedly remove an unused variable 2011-08-04 13:37:57 -04:00
f279b6bf7e Update Spanish translation
Forgotten "%p" in 12h time formats.
2011-08-04 12:57:31 +02:00
daec53f4fe Update Spanish translation
The mistery of the combining accutes continues.

Signed-off-by: Diego Escalante Urrelo <descalante@igalia.com>
2011-08-04 12:39:28 +02:00
cb1966612e Don't save unacked messages
Don't bother tracking which messages we need to ACK, just tell Telepathy to
ACK them all.

https://bugzilla.gnome.org/show_bug.cgi?id=654398
2011-08-04 10:53:48 +02:00
cda279f5c2 Updated Spanish translation 2011-08-04 09:31:13 +02:00
31915cdc93 Updated Vietnamese translation 2011-08-04 09:16:04 +07:00
329f803004 po/vi.po: import from Damned Lies 2011-08-04 09:10:38 +07:00
7780c99de6 telepathyClient: Use markup for timestamps
Commit aa1405e4ea introduced <b> tags for timestamps, but forgot to enable
markup with them.
2011-08-03 21:53:15 -04:00
d3ad857ba4 build: gnome-control-center needs latest gnome-menus 2011-08-03 21:46:00 -04:00
aa1405e4ea telepathyClient: Add our own translations for timestamps
As an effort to prevent a string freeze to land timestamps on 3.0, we reused
translations for the calendar. Now that the string freeze is long gone, make
some proper strings.

https://bugzilla.gnome.org/show_bug.cgi?id=640271
2011-08-03 18:14:34 -04:00
aa0a7f7816 Updated Spanish translation 2011-08-03 21:54:48 +02:00
3850edced5 notificationDaemon: fix syntax error 2011-08-03 15:21:00 -04:00
ddd59f2e76 notification-daemon: Add support for 'default' actions
The notification spec supports the concept of a 'default' action:
  "The default action (usually invoked my clicking the notification)
   should have a key named "default". The name can be anything, though
   implementations are free not to display it."
Support this by invoking the 'default' action rather than a emitting
the 'clicked' signal when clicking notifications which specifie a
default action.
Also don't add an action button for the default action.

https://bugzilla.gnome.org/show_bug.cgi?id=655818
2011-08-03 20:07:50 +02:00
10a0f2b614 altTab: do a step transition instead of fading in
Commit 7596fdb460 makes the popup feel slow.
Instead of fading the popup in we wait a bit and show it instantly.

https://bugzilla.gnome.org/show_bug.cgi?id=652346
2011-08-03 16:23:35 +01:00
8bc85d4a79 main: Add Main.notify() for simple system messages
... similar to Main.notifyError(), but don't duplicate the message
on stderr/in the log.

https://bugzilla.gnome.org/show_bug.cgi?id=652718
2011-08-03 17:19:18 +02:00
3dc07d48c5 shell: fail cleanly if XFixesGetCursorImage fails
from Mageia, via Olav Vitters
https://bugzilla.gnome.org/show_bug.cgi?id=653119
2011-08-03 11:12:32 -04:00
c1acf992fa layout: make Chrome an implementation detail of LayoutManager
Make the Chrome object be a private member of LayoutManager, and add
new LayoutManager methods to proxy to the old Chrome methods.

https://bugzilla.gnome.org/show_bug.cgi?id=655813
2011-08-03 09:18:30 -04:00
99149f9c41 layout: merge chrome.js into layout.js
LayoutManager and Chrome are already somewhat intertwined and will be
becoming more so. As a first step in merging them, move the Chrome
object into layout.js (with no other code changes).

https://bugzilla.gnome.org/show_bug.cgi?id=655813
2011-08-03 09:18:30 -04:00
bcd307066a lookingGlass: put this in the chrome layer
Looking Glass is supposed to slide out from underneath the panel.
Rather than fiddling with Main.chrome.actor directly, just add the lg
actor to the chrome, and fix its stacking there.

https://bugzilla.gnome.org/show_bug.cgi?id=655813
2011-08-03 09:18:30 -04:00
446910cb10 messageTray: move the summary notification out of MessageTray.actor
With the old pre-boxpointer summary notifications, it sort of made
sense that the summary notification actor was a child of the message
tray. But there's no reason for that now, and in fact, it ends up
requiring special cases in some places since hovering over the summary
notification counts as hovering over the tray. So, fix this.

https://bugzilla.gnome.org/show_bug.cgi?id=655813
2011-08-03 09:18:18 -04:00
fde200d084 panel: move the corners into the panel actor
Rather than having the panel corners as independent bits of chrome and
manually syncing their positions, put them inside the panel actor, and
update the panel's allocation code to position them correctly.

https://bugzilla.gnome.org/show_bug.cgi?id=655813
2011-08-03 09:17:52 -04:00
a376cd1610 chrome: Make affectsStruts default to false
Since we only want it to be true for the panel, and nothing else.

https://bugzilla.gnome.org/show_bug.cgi?id=655813
2011-08-03 09:17:52 -04:00
dbeab0ef87 St: fix container paint volumes
If a container is not clip-to-allocation, then its get_paint_volume()
needs to include the paint volumes of all of its children, since they
(or their children) may paint outside the container's allocation.

Also, if the superclass get_paint_volume() returns FALSE, then the
subclass should return FALSE too.

https://bugzilla.gnome.org/show_bug.cgi?id=655812
2011-08-03 09:16:55 -04:00
7765d6a08f shell-global: remove "gratuitous" meta_plugin_* calls
MetaPlugin wraps a bunch of compositor (and plain metacity) methods
that we can just call ourselves, so just do that. (Presumably this
dates back to some ancient time when it was imagined that plugins
wouldn't need access to the full metacity API.)

https://bugzilla.gnome.org/show_bug.cgi?id=654639
2011-08-03 09:09:55 -04:00
aed50e2a39 shell-global: add a "display" property
and update callers to fetch that rather than doing
"global.screen.get_display()"

https://bugzilla.gnome.org/show_bug.cgi?id=654639
2011-08-03 09:09:55 -04:00
b262a42458 shell-global: keep better track of screens and displays
Rather than constantly asking mutter for the MetaScreen, and then
figuring out the MetaDisplay/Display/etc from there, just keep track
of everything we care about inside ShellGlobal.

https://bugzilla.gnome.org/show_bug.cgi?id=654639
2011-08-03 09:09:55 -04:00
3fd90dfcb1 polkit: Update style of password entry
Use a subtle gradient background and add a blue border when focused.

https://bugzilla.gnome.org/show_bug.cgi?id=655422
2011-08-03 12:57:42 +02:00
dec55a3291 theme: make modal dialog buttons match the mockups
The buttons should have a glassy transparent look. Also, they should not
be as tall, should light up on hover, and their labels should be white
in order to stand out. Making the labels solid white requires removing the
transparency set in modalDialog.js. Also, add a separate color setting
for the dialog as a whole - this avoids having a white icon.

https://bugzilla.gnome.org/show_bug.cgi?id=655428
2011-08-03 11:23:22 +01:00
f6f3ded842 Added Malay translation 2011-08-03 10:17:22 +02:00
004c5cf287 shell-app-system: Load settings apps
When porting to the new gnome-menus API in commit 8f3bdd4f1, the
initial loading of settings apps was left out, so settings panels
are neither found nor can be launched from the top panel menus.
2011-08-02 22:17:50 +02:00
aba5f2f7b8 Theme: match message tray and panel box pointers
Panel box pointers were recently updated to match the mockups. Message
tray box pointers should look the same. Update the corner radius, stroke
colour and pointer dimensions to match.

https://bugzilla.gnome.org/show_bug.cgi?id=655627
2011-08-01 20:54:42 +01:00
2a96964204 st-container: fix a misspelled variable name 2011-08-01 14:29:15 -04:00
a9fe2a1493 gnome-shell.modules: Import gnome-menus 2011-08-01 14:16:13 -04:00
8f3bdd4f1a Port to new gnome-menus API
Since gnome-menus is now introspectable, eventually we can drop
ShellAppSystem entirely.  For now though, just do the basic port.

https://bugzilla.gnome.org/show_bug.cgi?id=648149
2011-08-01 13:42:17 -04:00
4322a20cbe Added StButtonAccessible
Basic skeleton and the proper role (ATK_ROLE_PUSH_BUTTON)
2011-08-01 19:20:08 +02:00
2403fd0680 panelMenu: add a gap between the panel and its menus
The specs call for a 2 pixel gap between the panel and its menus,
though we need to specify this as 4 pixels, since it's relative to the
bottom of the icon/title, not the bottom of the panel (up until now,
the point of the menu arrow was actually overlapping the menu's
highlight underline).

Also, move the gap specification into the CSS, since it makes more
sense there.

https://bugzilla.gnome.org/show_bug.cgi?id=655627
2011-08-01 11:48:02 -04:00
e01baf2a25 Fix critical when setting a NULL label
st_label_set_text() does not accept NULL, and emits a g_critical
in that case.

https://bugzilla.gnome.org/show_bug.cgi?id=654638
2011-08-01 16:40:25 +02:00
d27b37fefe Updated POTFILES.in 2011-08-01 15:42:24 +02:00
acedb60abe Theme: improve menu popup appearance
Make menu popups fully match the mockups by changing the stroke
colour, pointer and corner radius size. This has the benefit of
distinguishing the menu from the background and gives it a subtler
appearance that fits the shell's aesthetic.

https://bugzilla.gnome.org/show_bug.cgi?id=655627
2011-07-30 17:04:19 +01:00
77556d181e Use "Region and Language Settings" label
Since this link in the keyboard menu points to Region and Language
Settings in System Settings, we should be consistent and use that
term instead of "Localization Settings"

Also, this removes ellipsis from "Show Keyboard Layout" since it
doesn't require further input.

https://bugzilla.gnome.org/show_bug.cgi?id=652984
2011-07-29 14:41:08 +02:00
60612cace9 Add markup.js to TEST_JS 2011-07-27 19:13:03 -04:00
f057502834 Require GJS 1.29.15 2011-07-27 19:13:03 -04:00
21a1149532 Always include gnome-shell-jhbuild.in in EXTRA_DIST 2011-07-27 19:13:03 -04:00
6724ca4f63 Bump version to 3.1.4
Update NEWS
2011-07-27 19:13:03 -04:00
7a6c25b3fb chrome: Ignore minimized windows when updating visibility
The current check for fullscreen windows ignores the window's
minimization state, so that chrome which is hidden in fullscreen
will always hide if the window on top of the window stack is
fullscreen, even if it is actually minimized.
Instead, skip minimized windows when looking for fullscreen windows.

https://bugzilla.gnome.org/show_bug.cgi?id=655446
2011-07-27 23:40:49 +02:00
0366e320af NotificationDaemon: only remove the source if notification sender is removed from DBus and the application is set
This ensures that we don't remove "notify-send" sources, senders of which are
removed from DBus immediately.
2011-07-27 17:16:05 -04:00
f03793b825 tests: add a test for MessageTray._fixMarkup
https://bugzilla.gnome.org/show_bug.cgi?id=650298
2011-07-27 09:29:03 -04:00
0907f030b4 tests: fix up typelib include paths
The js modules have so many imports back and forth that it's pretty
much guaranteed that if you import even one of them, you'll end up
importing all of them, including ui.status.bluetooth and
ui.status.network. So fix up the typelib include paths the same way
gnome-shell-jhbuild does, so we can find everything.

https://bugzilla.gnome.org/show_bug.cgi?id=650298
2011-07-27 09:29:03 -04:00
7542e68b6f messageTray: improve bad-markup handling
_fixMarkup() was supposed to be ensuring that the markup we passed to
clutter was correct, but it was validating the syntax incorrectly, and
wasn't checking that the markup was valid (or even well-formed). This
is bad because if you pass bad pango markup to
clutter_text_set_markup(), it will g_warn and drop the string on the
floor.

Fix by fixing up the regexps, and then calling Pango.parse_markup() on
the result, and just xml-escaping everything if parse_markup() fails.

https://bugzilla.gnome.org/show_bug.cgi?id=650298
2011-07-27 09:29:03 -04:00
9003a34285 Updated Slovak translation 2011-07-26 15:12:15 +02:00
312 changed files with 78020 additions and 32868 deletions

16
.gitignore vendored
View File

@ -18,9 +18,24 @@ config
configure
data/gnome-shell.desktop
data/gnome-shell.desktop.in
data/gnome-shell-extension-prefs.desktop
data/gnome-shell-extension-prefs.desktop.in
data/gschemas.compiled
data/org.gnome.shell.gschema.xml
data/org.gnome.shell.gschema.valid
docs/reference/*/*.args
docs/reference/*/*.bak
docs/reference/*/*.hierarchy
docs/reference/*/*.interfaces
docs/reference/*/*.prerequisites
docs/reference/*/*.sgml
docs/reference/*/*.signals
docs/reference/*/*.stamp
docs/reference/*/*.txt
docs/reference/*/*.types
docs/reference/*/html/
docs/reference/*/xml/
gtk-doc.make
js/misc/config.js
intltool-extract.in
intltool-merge.in
@ -49,6 +64,7 @@ src/calendar-server/org.gnome.Shell.CalendarServer.service
src/gnome-shell
src/gnome-shell-calendar-server
src/gnome-shell-extension-tool
src/gnome-shell-extension-prefs
src/gnome-shell-hotplug-sniffer
src/gnome-shell-jhbuild
src/gnome-shell-perf-helper

View File

@ -1,7 +1,7 @@
# Point to our macro directory and pick up user flags from the environment
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
SUBDIRS = data js src tests po man
SUBDIRS = data js src browser-plugin tests po man docs
EXTRA_DIST = \
.project \
@ -19,3 +19,5 @@ DIST_EXCLUDE = \
distcheck-hook:
@echo "Checking disted files against files in git"
@$(srcdir)/tools/check-for-missing.py $(srcdir) $(distdir) $(DIST_EXCLUDE)
DISTCHECK_CONFIGURE_FLAGS = --enable-gtk-doc

549
NEWS
View File

@ -1,3 +1,552 @@
3.3.5
=====
* Extension system: [Jasper; #668429]
http://blog.mecheye.net/2012/02/more-extension-api-breaks/
- Add a 'gnome-shell-extension-prefs' application for displaying extension
preferences as provided by the extension in a prefs.js file.
- Allow launching gnome-shell-extension-prefs from extensions.gnome.org
throuhg the browser plugin.
- Add ExtensionUtils.getCurrentExtension() for an extension to get a
handle to an extension object, to get local imports or paths.
- Add an onshellrestart callback to the browser plugin [Jasper; #668517]
* Screenshots:
- Move the screenshot "flash" to the shell [Cosimo; #668618]
- Move saving screenshots to a thread [Adel Gadllah; #652952]
- Correctly screenshot rounded decorations [Jasper; #662486]
* Screen recorder:
- Change the default pipeline to favor speed over quality [Owen; #669066]
- Drop frames to keep from running the user out of memory [Owen; #669066]
* Work around a slow implementation of glReadPixels() in the Intel drivers,
improving performance for screenshots and the screen recorder.
[Owen; #669065]
* Use Keywords: field in desktop files when search for applications
[Florian; #609702]
* Strip debian- when matching desktop file names [Jasper; #665647]
* Fix up various problems from CSS background size-addition
[Florian, Jasper; #668430, #633462]
* UI tweaks and behavior fixes
[Florian, Giovanni, Stefano; #643867, #666197, #664622]
* Some improvements to exported accessibility information [Alejando; #667376]
* Don't show contacts without IM information as offline [Florian; #662685]
* Don't change status from hidden to extended_away when going idle
[Florian; #642408]
* Cleanups [Emmanuele, Jasper; #662152, #669239]
* Misc bug fixes [Cosimo, Dan, Florian, Jasper, Rui, Stefano;
#633462, #643867, #662213, #662386, #662747, #665000, #665017, #665322,
#667371, #667860, #668020, #668517, #668541, #669236]
Contributors:
Emmanuele Bassi, Giovanni Campagna, Cosimo Cecchi, Stefano Facchini,
Rui Matos, Florian Müllner, Alejandro Piñeiro, Jasper St. Pierre,
Owen Taylor, Dan Winship
Translations:
Daniel Mustieles [es], Timo Jyrinki [fi], Seán de Búrca [ga],
Fran Diéguez [gl], Kjartan Maraas [nb], Wouter Bolsterlee [nl],
Danishka Navin [si], Yaron Shahrabani [he], Matej Urbančič [sl],
Chao-Hsiung Liao [zh_HK, zh_TW]
3.3.4
=====
* https://live.gnome.org/EveryDetailMatters
- Add "browse" for labels for dash items - once a tooltip is
showing, switch to other items without a delay [Seif; #666170]
- Always scale down windows in the overview at least to 70% [Vit; #646704]
- Fix the new-workspace drop indicator sometimes getting stuck
[Stefano; #664201]
- Delay rearranging windows in the overview as long as the pointer
is over a window [Vit; #645325]
* Add a GConf => DConf migration file for overriden Mutter settings
[Florian; #667636]
* When a VPN connection is active, show that as the network icon
[Giovanni; #665115]
* Handle the "ExtendedAway" IM status as away, not offline [Guillaume; #667813]
* Improve the appearance of the labels in "Applications" [Alex; #642392]
* Adjust for GTK+ and Mutter API changes for application menu [Ryan; #668118]
* Add section label support to the application menu [Giovanni; #666681]
* Fix screenshot methods to work again [Cosimo; #667662]
* Fix several crashers related to updating workspace thumbnails [Owen; #667652]
* Fix memory management error causing gnome-shell-hotplug-sniffer to crash
[Owen; #667378]
* Build fixes [Emmanuele, Rico; #667864]
* Code cleanups [Adel; #668087]
* Misc bug fixes [Colin, Florian, Giovanni, Owen, Xavier; #633028, #658817,
#664138, #667881, #668048, #668050]
Contributors:
Emmanuele Bassi, Giovanni Campagna, Cosimo Cecchi, Xavier Claessens,
Guillaume Desmottes, Stefano Facchini, Adel Gadllah, Alex Hultman,
Ryan Lortie, Seif Lotfy, Florian Müllner, Vit Stanislav, Owen Taylor,
Rico Tzschichholz, Colin Walters
Translations:
Ihar Hrachyshka [be], Alexander Shopov [bg], Arash Mousavi [fa],
Jiri Grönroos, Timo Jyrinki [fi], Fran Diéguez [gl], Kjartan Maraas [nb],
Yuri Myasoedov [ru], Matej Urbančič [sl], Nguyễn Thái Ngọc Duy [vi]
3.3.3
=====
* https://live.gnome.org/EveryDetailMatters
- Stop flashing the window labels on actions in overview [Zan; #644861]
- Improve the look of window captions in the overview [Marc; #664487]
- Move dash tooltips beside the icon [Seif, Stefano; #666166]
* Support application menus exported from applications via new GLib API
and D-Bus protocol. [Giovanni, Colin, Matthias, Cosimo]
* For removable device prompts, show "Open with Rhythmbox], rather
than "Open with Rhythmbox Music Player' [Florian; #664561]
* Switch to activating the message tray only with a hot corner rather
than a full row of pixels, allowing mouse events to apps [Rui; #663366]
* Fully handle the case where the workspaces-only-on-primary
GSetting is set to false [Florian; #652580]
* Add support for background-size CSS property to St [Quentin; #633462]
* Port to new GJS Lang.Class framework [Giovanni; #664436]
* Finish port to GDBus [Giovanni; #664436]
* Stop using the deprecated Clutter default stage [Florian, Jasper; #664052]
* Fix bugs that kept browser plugin from working in WebKit-based browser
[Jasper; #666444]
* Fix typo that made uninstalling extensions not work [Jasper]
* Fix crash in browser plugin if shell is not run [Jürg]
* Reintroduce piscine paschal ovum [Giovanni; #666606]
* Network menu bug fixes
Giovanni; #664124, #665194, #665680, #666429, #666614]
* Misc bug fixes [Florian, Jasper, Jonny, Marina, Ron; #647587, #659272,
#664138, #665261, #666020, #666243]
* Build fixes [Owen]
Contributors:
Jürg Billeter, Giovanni Campagna, Stefano Candori, Cosimo Cecchi,
Matthias Clasen, Zan Dobersek, Quentin Glidic, Jonny Lamb, Ryan Lortie,
Seif Lotfy, Rui Matos, Florian Müllner, Bastien Nocera, Jasper St. Pierre,
Marc Plano-Lesay, Owen Taylor, Colin Walters, Ron Yorsten,
Marina Zhurakhinskaya
Translations:
Petr Kovar [cz], Kris Thomsen [dk], Daniel Mustieles [es],
Ville-Pekka Vainio [fi], Yaron Shahrabani [he], Luca Ferretti [it],
Hideki Yamane [ja], Žygimantas Beručka [lt], Jovan Naumovski [mk],
Kjartan Maraas [nb], "Andreas N" [nn], Lucian Adrian Grijincu [ro],
Matej Urbančič [sl], Praveen Illa [te], Muhammet Kara [tr],
Daniel Korostil [uk], Aron Xu [zh_CN]
3.3.2
=====
* Port D-Bus usage in the shell to GDBus [Giovanni, Marc-Antoine, Florian,
Jasper, Matthias; #648651, #658078, #663902, #663941]
* Message tray
- Add right-click option to chats to mute the conversation [Ana; #659962]
- Don't steal the focus when popping up under the pointer [Rui; #661358]
* Looking Glass
- Add alt-Tab completion [Jason; #661054]
- Show errors from extensions in the extensions tab [Jasper; #660546]
- Allow switching tabs with <Control>PageUp/PageDown
- Theme consistently with the rest of the shell [Jason; 650900]
* Extension system
- Don't try to load disabled extensions at all [Jasper; #661815, #662704]
- Enable and disable plugins in a consistent order [Jasper; #661815, #662704]
- Add options to enable/disable extensions to gnome-shell-extension-tool
[Jasper; #661815]
* Adapt to Mutter change to GSettings [Florian, Matthias; #663429]
* Allow creating a new workspace by dragging a window or launcher in the
middle of two existing ones [Jasper; #646409]
* Allow using Alt-Tab while during drag-and-drop and other operations
that grab the pointer [Adel; #660457]
* Do a better job of finding the right user to authenticate
as when showing a PolKit dialog [Matthias; #651547]
* Control the D-Bus Eval() method by the developer-tools GSetting which
is used for looking glass and screen recorder. [Jasper; #662891]
* Fix browser plugin to work under WebKit-based browser [Jasper; #663823]
* Fix certain stacking issues with alt-Tab [Jasper; #660650]
* Fixes for GLib deprecations [Jasper; #662011]p
* Fixes for GTK+ deprecations [Florian, Rico; #662245]p
* Fixes for Clutter deprecations [Jasper; #662627]
* Visual improvements and UI tweaks [Florian, Jakub, Jasper;
#662800, #658096, #662226]
* Hard-code "Home" as the name for the home dir, rather than looking
it up via GSettings; avoids schema dependency [Cosimo; #559895]
* Don't show "Switch User" on single user machines [Florian; #657011]
* Generate documentation for St toolkit [Florian]
* Improve marking of strings for translation [Matthias, Piotr; #658664]
* Networking menu bug fixes [Giovanni; #650007, #651378, #659277, #663278]
* Code cleanups and leak fixes to StTextureCache
[Jasper, Florian; #660968, #662998]
* Code cleanups [Adel, Florian, Jasper; #662238, #663584]
* Build fixes [Adel, Colin, Florian, Ming Han]
* Misc bug fixes [Adel, Florian, "Fry", Jasper, Giovanni, Ray, Rui, Stefan;
#660520, #661029, #661231, #661623, #661921, #662235, #662236, #662502,
#662394, #662799, #662969, #663175, #663277, #663815, #663891, #662967]
Contributors:
Giovanni Campagna, Cosimo Cecchi, Matthias Clasen, Piotr Drąg, Adel Gadllah,
Rui Matos, Florian Müllner, Marc-Antoine Perennou, Ana Risteska,
Jason Siefken, Jakub Steiner, Ray Strode, Jasper St. Pierre, Ming Han Teh,
Rico Tzschichholz, Colin Walters, Stefan Zwanenburg
Translation:
Alexander Shopov [bg], Marek Černocký [cs], Mario Blättermann [de],
Kostas Papadimas [el], Bruce Cowan [en_GB], Kristjan Schmidt [eo],
Jorge González, Daniel Mustieles, Benjamín Valero Espinosa [es],
Mattias Põldaru [et], Arash Mousavi [fa], Ville-Pekka Vainio [fi],
Fran Diéguez [gl], Yaron Shahrabani [he], Hideki Yamane [ja],
Algimantas Margevičius [lt], Kjartan Maraas [nb], Daniel Nylander [se],
Matej Urbančič [sl], Praveen Illa [te], Muhammet Kara [tr],
Nguyễn Thái Ngọc Duy [vi], Cheng-Chia Tseng [zh_HK, zh_TW]
3.2.1
=====
* Restore the IM state on startup - if you were available in when you logged
out, then you'll be set available again when you log in.
[Florian; #65902, #661485]
* Improve searching for contacts in the overview: search more fields,
show a more meaningful name, require that all search terms match.
[Florian, Matthias; #660580]
* Improve search for applications in the overview: take frequency into
account and tweak match algorithm [Florian; #623372]
* Remove the "Show Password" switch from network password prompts, and
move the functionality to a right-click menu [Florian; #658948]
* Add context menus with Cut/Paste options to most entries [Florian; #659275]
* On screen keyboard:
- Show the keyboard immediately when it's turned enabled [Dan; #659743]
- Fix problem where keyboard would hide when starting to type
in the search entry [Nohemi; #661340]
- Fix problem with keyboard hiding when selected accented characters
[Nohemi; 661707]
* Login mode:
- Allow hitting Enter to select the first user [Ray; #657996]
- Fix flicker of a fingerprint prompt that could show up [Ray; #660492]
- Fix password bullets vanishing during login [Ray; #657894]
- Misc bug fixes and visual tweaks [Ray; #659763, #660919, #661479]
* Display a caps-lock warning in password entries [Florian; #660806]
* Show the state of installed extensions in Looking Glass [Jasper; #660494]
* Load user extensions after system ones [Jasper; #661815]
* Fix problem with many applications showing extra-large icons in
notifications [Marina; #659158]
* Fix a problem where alt-Tab had trouble tracking the current
application with certain applications such as Emacs. [Dan; #645026]
* Fix confusion between different users avatar images [Florian; #660585]
* Remove behavior where you could switch workspaces by bumping
a dragged window in the overview against a screen edge; it was
leftover and just confusing. [Florian; #660838]
* Fix long-standing bug where the Dash in the overview could end up mis-sized
and run off the screen [Florian; #649248]
* Fix automatic launching of applications when media is inserted
[Cosimo; #660821]
* Fix handling of vertically stacked monitors with NVIDIA drivers
[Florian; #661387]
* Translation marking fixes [Jasper, Wouter; #660600]
* Code cleanups and warning fixes [Adel, Dan, Florian, Jasper;
#659822, #659940, #660122, #660358, #660968, #661231]
* Small memory leak fixes [Florian, Jasper; #661231]
* Misc bug fixes [Adel, Florian, Jasper; #659274, #659861, #660166, #660310,
#660397, #660608, #660606, #660674, #660774. #660848, #661151, #661617]
Contributors:
Wouter Bolsterlee, Cosimo Cecchi, Matthias Clasen, Nohemi Fernandez,
Adel Gadllah, Florian Müllner, Jasper St. Pierre, Ray Strode, Dan Winship,
Marina Zhurakhinskaya
Translations:
Tiffany Antopolski [eo], Xandru Armesto [ast], Alexander Shopov,
Ivaylo Valkov [bg], Gil Forcada [ca], Carles Ferrando [ca@valencia],
Mario Blättermann, Paul Seyfert [de], Bruce Cowan [en_GB],
Jorge González, Daniel Mustieles [es], Arash Mousavi [fa], Bruno Brouard [fr],
Seán de Búrca [ga], Fran Diéguez [gl], Gabor Kelemen [hu], Luca Ferretti [it],
Takayuki Kusano [ja], Changwoo Ryu [ko], Erdal Ronahi [ku],
Algimantas Margevičius [lt], Rudolfs Mazurs [lv], Wouter Bolsterlee [nl],
Piotr Drąg [pl], Adorilson Bezerra [pt_BR], Yuri Myasoedov [ru],
Matej Urbančič [sl], Daniel Nylander [sv], Miroslav Nikolić [sr, sr@latin],
Tirumurti Vasudevan [ta], Krishnababu Krothapalli [te], Daniel Korostil [uk],
Nguyễn Thái Ngọc Duy [vi], YunQiang Su [zh_CN]
3.2.0
=====
* Prevent the fallback on-screen keyboard from showing up while
GNOME Shell is running [Dan, #659865]
* Disable code to reposition windows around the on-screen keyboard;
it wasn't finished or working properly. [Dan; #659643]
* Fix interaction between on-screen keyboard and notifications
[Dan; #658603]
* Fix menu-sizing problems in right-to-left locales. [Florian; #659827]
* Update chat icons in the message tray when an avatar image changes
[Marina; #659768]
* Fix problem with empty notification bubbles being left [Marina; #659862]
* Fix problem with chat notifications bouncing when new messages come in.
[Marina; #659768]
* Fix bug that was causing SIP calls to automatically be accepted in some
circumstances [Guillaume; #660084]
* Fix string that should have been marked translatable [Frédéric]
* Fix a crash that could happen during CSS transitions [Florian; #659676]
* Build fixes [Colin, Florian]
Contributors:
Guillaume Desmottes, Florian Müllner, Frédéric Péters, Colin Walters,
Dan Winship, Marina Zhurakhinskaya
Translations:
Friedel Wolff [af], Nilamdyuti Goswami [as], Ihar Hrachyshka [be],
Ivaylo Valkov [bg], Gil Forcada [ca], Carles Ferrando [ca@valencia],
Petr Kovar [cz], Mario Blättermann [de], Kris Thomsen [dk],
Tiffany Antopolski, Kristjan Schmidt [eo], Daniel Mustieles [es],
Inaki Larranaga Murgoitio [eu], Tommi Vainikainen [fi], Bruno Brouard [fr],
Fran Dieguez [gl], Yaron Shahrabani [he], Gabor Kelemen [hu],
Andika Triwidada [id], Jiro Matsuzawa [ja], Changwoo Ryu [ko],
Rudolfs Mazurs [lv], Aurimas Černius [lt], Kjartan Maraas [nb],
A S Alam [pa], Piotr Drąg [pl], Duarte Loreto [pt], Djavan Fagundes,
Rodolfo Ribeiro Gomes, Gabriel F. Vilar [pt_BR], Yuri Myasoedov [ru],
Daniel Nylander [se], Martin Srebotnjak [sl], Michal Štrba [sv],
Krishnababu Krothapalli, Praveen Illa [te], Cheng-Chia Tseng [zh_KH, zh_TW]
3.1.92
======
* Login screen
- Add the ability to set a logo at the top of the user list [Ray; #658062]
- Add fingerprint reader support [Ray; #657823]
- Add a power button offering the choice of Suspend/Restart/Power off
[Ray; #657822]
- Remove the option to view the current keyboad layout [Matthias; #659164]
- Make Control-Alt-Tab work for full keyboard access [Ray; #659177]
* Frequently initiate a full garbage collection; Spidermonkey isn't very good
at tracking the amount of resources we have allocated so this hopefully will
improve memory usage without affecting performance too much [Colin; #659254]
* Stop adding a notification when the network connection is lost
[Colin; #658954]
* When disabling notifications; display a notification
"Your chat status will be set to busy" [Florian; #652718]
* Fix keynav in network dialogs [Florian; #659133]
* Improve calendar styling [Sean; #641135, #651299]
* Shrink padding around panel buttons for narrow screens [Dan; #651299]
* Allow enabling the onscreen keyboard through the accessibility menu
[Dan; #612662]
* Fix problem that was causing VPN secret dialogs to be delayed before showing
[Florian; #658484]
* Make custom-keybindings for the window switcher that don't use alt
work correctly [Florian; #645200]
* Fix duplicate application icons in the Activities Overview [Colin; #659351]
* Bug fixes for dimming windows with attached modal dialogs
[Jasper, Owen; #659302, 659634]
* Add build-time support for BROWSER_PLUGIN_DIR environment variable
[Vincent; #659123]
* Build fixes [Vincent; #659194]
* Code cleanups and test cases
[Adel, Dan, Florian, Jasper; #651299, #658092, #658939]
* Misc bug fixes
[Adel, Colin, Cosimo, Dan, Florian, Giovanni, Jasper, Ray, Xavier;
#651299, #652837, #657249, #658004, #658150, #658239, #658469, #658598,
#658605, #659050, #659159, #659210, #659270, #659370, #659633]
Contributors:
Giovanni Campagna, Cosimo Cecchi, Xavier Claessens, Matthias Clasen,
Rui Matos, Florian Müllner, Jasper St. Pierre, Owen Taylor,
Vincent Untz, Colin Walters, Sean Wilson, Dan Winship
Translations:
Ihar Hrachyshka [be], Alexander Shopov, Ivaylo Valkov [bg],
Mario Blättermann [de], Jorge González, Daniel Mustieles [es],
Arash Mousavi [fa], Ville-Pekka Vainio [fi], Fran Dieguez [gl],
Sweta Kothari [gu], Gabor Kelemen [hu], Jiro Matsuzawa [ja],
Luca Ferretti [it], Rudolfs Mazurs [lv], Kjartan Maraas [nb], A S Alam [pa],
Piotr Drąg [pl], Duarte Loreto [pt], Yuri Myasoedov [ru],
Daniel Nylander [se], Matej Urbančič [sl], Miroslav Nikolić [sr, sr@latin],
Michal Štrba [sv], Tirumurti Vasudevan [ta], Phương Lê Hoàng [vi],
Aron Xu [zh_CN], Chao-Hsiung Liao [zh_HK, zh_TW]
3.1.91.1
========
* Add a browser plugin - this plugin, tied to extensions.gnome.org,
allows users to download and install shell extensions, and enable,
disable, and uninstall extensions they already have installed.
[Jasper; #658070, #658612]
* Improve adding links to URLs in notifications [Dan; #636252]
* Remove "connection lost" notifications after reconnecting [Giovanni; #658049]
* Hide the onscreen keyboard when leaving a text entry [Dan; #658591]
* Fixes for translated strings [Florian; #639987, #644097, #645037]
* Bug fixes for network menu [Florian; #658492]
* Code cleanup [Dan; #646934]
* Build fixes [Javier, Rico]
* Misc bug fixes [Emmanuele, Florian, Jasper, Marina, Matthias, Ray;
#652837, #658423, #658503, #658525, #658562, #658624, #658640, #658983]
Conributors:
Emmanuele Bassi, Giovanni Campagna, Matthias Clasen, Javier Jardón,
Florian Muellner, Jasper St. Pierre, Ray Strode, Rico Tzschichholz,
Dan Winship, Marina Zhurakhinskaya
Translations:
Ihar Hrachyshka [be], Bruce Cowan [en_GB], Jorge González,
Daniel Mustieles [es], Timo Jyrinki [fi], Bruno Brouard, Luc Guillemin,
Claude Paroz, Luc Pionchon [fr], Fran Dieguez [gl], Rajesh Ranjan [hi],
Andika Triwidada [id], Luca Ferretti [it], Changwoo Ryu [ko],
Rudolfs Mazurs [lt], Kjartan Maraas [nb], Manoj Kumar Giri [or],
A S Alam [pa], Piotr Drąg [pl], Duarte Loreto [pt], Henrique P. Machado,
Gabriel F. Vilar [pt_BR], Daniel Nylander [se], Matej Urbančič [sl],
Tirumurti Vasudevan [ta], Yinghua Wang [zh_CN],
Chao-Hsiung Liao [zh_HK, zh_TW]
3.1.91
======
* Fix problem with applications vanishing from alt-Tab when
desktop files change. [Colin; #657990]
* Fix interaction of on-screen keyboard with run-dialog and
Looking Glass console [Dan; #657986]
* Add public API for adding and removing search providers
[Philippe; #657548, #658113]
* Allow changing IM status with scroll wheel [Florian; #657973]
* Limit volume slider to 100% [Bastien; #657607]
* Change "Do Not Disturb" to "Notifications" in user menu [Florian; #652718]
* Switch browser in default favorites to Epiphany [Colin; #650616]
* Misc bug fixes [Dan, Florian, Jasper, Marc-Antoine, Rui;
#649631, #655069, #656142, #657703, #657759, #658007, #658065, #658176]
Contributors:
Rui Matos, Florian Müllner, Philippe Normand, Marc-Antoine Perennou,
Jasper St. Pierre, Colin Walters, Dan Winship
Translations:
Ihar Hrachyshka [be], Mario Blättermann [de], Kris Thomsen [da],
Jorge González [es], Arash Mousavi [fa], Fran Dieguez [gl],
Takayuki Kusano [ja],Aurimas Černius [lt], Kjartan Maraas [nb], A S Alam [pa],
Stas Solovey [ru], Daniel Nylander [se], Tirumurti Vasudevan [ta],
Chao-Hsiung Liao [zh_HK, zh_TW]
3.1.90.1
========
* Fix typo that was breaking the "Login Screen" mode [Marc-Antoine]
* Fix build with new gobject-introspection [Dan]
* Use a better icon for removable devices [Cosimo; #657757]
* Add support for asynchronous search provides [Philippe, Jasper, Seif; #655220]
* Misc bug fixes [Alex, Guillaume, Jasper; #657657, #657696]
* Misc build fixes [Adel; #657697]
Contributors:
Cosimo Cecchi, Guillaume Desmottes, Adel Gadllah, Alexander Larsson, Seif Lotfy,
Philippe Normand, Marc-Antoine Perennou, Jasper St. Pierre, Dan Winship
Translations:
Jorge González, Daniel Mustieles [es], Stas Solovey [ru]
3.1.90
======
* Add an on-screen keyboard that uses Caribou as a backend
[Nohemi, Dan; #612662]
* Allow searching for people in the overview using libfolks
as the backend [Morten; #643018]
* Add a "Login Screen" mode to be used when GDM is running; this
mode has a stripped down user interface, and also contains the
code to display the user list and authentication. [Ray; #657082]
* Rework user menu to separate out "Do Not Disturb" from the IM
status and to visually match GNOME Contacts. [Florian; #652837]
* Implement displaying images such as cover-art in notifications
[Neha, Marina; #621009]
* Support default actions for notifications [Florian; #655818]
* Networking
- Stop using nm-applet for dialogs; do them as proper system modal
dialogs in the shell code. [Giovanni; #650244]
- Fix handling of hidden access points [Giovanni; #646454]
* Telepathy integration
- Support subscription requests [Guillaume, Xavier; #653941]
- Notify on account connection errors [Alban, Jasper, Xavier; #654159]
- Allow approving file transfers [Guillaume; #653940]
- Improve styling of messages [Jasper; #640271]
* Extension system [Jasper; #654770]
- Support live enabling and disabling of extensions
- Add the ability to install extensions from HTTP
- Enhance D-Bus interface for controlling extensions
- Collect errors separately for each extension
* Add Main.panel.addToStatusArea for extension convenience
[Giovanni, Jasper, Marc-Antoine; #653205]
* Port to the new gnome-menus API. Clean up and speed up
application information loading [Colin; #648149, #656546]
* Use the accountsservice library rather than cut-and-pasted GDM code
[Florian; #650893]
* Add a D-Bus interface to take a screenshot; this will avoid various race
conditions with the current gnome-screenshot approach [Adel; #652952]
* Show numeric indicators to distinguish duplicate keyboard names
[Giovanni; #650128]
* Add GNOME Documents to the favorites list [Adel; #657520]
* Update the clock immediately on resume from suspend [Colin; #656403]
* Remove animation support from StAdjustment [Ray; #657082]
* Support configuration of calendar applications via gsettings
[Tassilo; #651190]
* Don't fade in alt-Tab - wait a bit and show it instantly [Rui; #652346]
* Darken workspace background on all workspaces [Rui; #656433]
* Improve detection of the starting day of the week [Florian; #649078]
* Add StButtonAccessible [Alejandro]
* Visual tweaks to match mockups
[Allan, Dan, Jasper, Marina; #640271, #655627, #655428, #656732]
* Misc bug fixes [Dan, Florian, Giovanni, Guillaume, Jasper, Jeremy, Rui;
#645708, #646761, #653119, #654398, #656125, #654707, #654898, #654638,
#656335, #657111]
* Code cleanups [Colin, Dan, Guillaume, Ray;
#652718, #654639, #648651, #655813, #657082]
* String tweaks [Jasper, Jeremy; #652984, #640271]
* Build fixes [Jasper, Nohemi; #644275, #655812]
Contributors:
Jeremy Bicha, Giovanni Campagna, Xavier Claessens, Alban Crequy,
Guillaume Desmottes, Allan Day, Neha Doijode, Nohemi Fernandez,
Tassilo Horn, Rui Matos, Morten Mjelva, Florian Müllner, Alejandro Piñeiro,
Jasper St. Pierre, Ray Strode, Colin Walters, Dan Winship,
Marina Zhurakhinskaya
Translations:
Ivaylo Valkov [bg], Mario Blättermann [de], Diego Escalante Urrelo,
Jorge González, Daniel Mustieles [es], Arash Mousavi [fa], Fran Dieguez [gl],
Yaron Shahrabani [he], Andika Triwidada, Wibiharto [id],
Aurimas Černius [lt], Umarzuki Bin Mochlis Moktar [ml], Kjartan Maraas [nb],
A S Alam [pa], Daniel Nylander [se], Ngô Chin, Nguyễn Thái Ngọc Duy [vi],
Aron Xu [zh_CN], Chao-Hsiung Liao [zh_HK, zh_TW]
3.1.4
=====
* Take over inserted media handling and autorun from gnome-session [Cosimo]
* Message Tray
- Display a count of unread notifications on icons
[Jasper, Guillaume; #649356, #654139]
- Only remove icons when the sender quits from D-Bus, not when it
closes its last window [Neha, Marina; #645764]
- Solve problems switching chats between shell and Empathy
[Guillaume; #654237]
- Fix handling of bad GMarkup in messages [Dan; #650298]
- Never show notifications when the screensaver is active [Dan; #654550]
* Telepathy integrationpp
- Implement Telepathy Debug interface to enable empathy-debugger
[Guillaume; #652816]
- Allow approving room invitations, and audio/video calls
[Guillaume; #653740 #653939]
- Send typing notifications [Jonny; #650196]
* Fix selection highlighting for light-on-dark entries [Jasper; #643768]
* Make control-Return in the overview open a new window [Maxim]
* Delay showing the alt-Tab switcher to reduce visual noise when
flipping betweeen windows [Dan; #652346]
* When we have vertically stacked monitors, put the message tray
on the bottom one [Dan; #636963]
* Fix various problems with keynav and the Activities button
[Dan; #641253 #645759]
* Ensure screensaver is locked when switching users [Colin; #654565]
* Improve extension creation tool [Jasper; #653206]
* Fix compatibility with latest GJS [Giovanni; #654349]
* Code cleanups [Adel, Dan, Jasper; #645759 #654577 #654791 #654987]
* Misc bug fixes [Richard, Dan, Florian, Giovanni, Jasper, Marc-Antoine, Rui;
#647175 #649513 #650452 #651082 #653700 #653989 #654105 #654791 #654267
#654269 #654527 #655446]
* Build fixes [Florian, Siegfried; #654300]
Contributors:
Giovanni Campagna, Cosimo Cecchi, Guillaume Desmottes, Neha Doijode,
Maxim Ermilov, Adel Gadllah, Siegfried-Angel Gevatter Pujals, Richard Hughes,
Jonny Lamb, Rui Matos, Florian Müllner, Marc-Antoine Perennou, Colin Walters,
Dan Winship, Marina Zhurakhinskaya
Translations:
Mario Blättermann, Paul Seyfert [de], Jorge González, Daniel Mustieles [es],
Fran Dieguez [gl], Yaron Shahrabani [he], Luca Ferretti [it],
Rudolfs Mazurs [lv], Kjartan Maraas [nb], A S Alam [pa], Yuri Kozlov [ru],
Michal Štrba, Matej Urbančič [sl]
3.1.3
=====
* Fix problem with "user theme extension" breaking the CSS for other

View File

@ -0,0 +1,21 @@
mozillalibdir = $(BROWSER_PLUGIN_DIR)
mozillalib_LTLIBRARIES = libgnome-shell-browser-plugin.la
libgnome_shell_browser_plugin_la_LDFLAGS = -module -avoid-version -no-undefined
libgnome_shell_browser_plugin_la_LIBADD = \
$(BROWSER_PLUGIN_LIBS)
libgnome_shell_browser_plugin_la_SOURCES = \
browser-plugin.c \
npapi/npapi.h \
npapi/npfunctions.h \
npapi/npruntime.h \
npapi/nptypes.h
libgnome_shell_browser_plugin_la_CFLAGS = \
$(BROWSER_PLUGIN_CFLAGS) \
-DG_DISABLE_DEPRECATED \
-DG_LOG_DOMAIN=\"GnomeShellBrowserPlugin\"

17
browser-plugin/README Normal file
View File

@ -0,0 +1,17 @@
The GNOME Shell Browser Plugin provides integration with gnome-shell and the
corresponding extensions repository, codenamed "SweetTooth". The plugin allows
the extensions repository to provide good integration, letting the website
know which extensions are enabled and disabled, and allowing the website to
enable, disable and install them.
Bugs should be reported at http://bugzilla.gnome.org against the 'gnome-shell'
product.
License
=======
The GNOME Shell Browser Plugin, like GNOME Shell itself is distributed under
the GNU General Public License, version 2 or later. The plugin also contains
header files from the "NPAPI SDK" project, tri-licensed under MPL 1.1, GPL 2.0
and LGPL 2.1. These headers are third-party sources and can be retrieved from:
http://code.google.com/p/npapi-sdk/

View File

@ -0,0 +1,946 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2011 Red Hat
*
* 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.
*
* Authors:
* Jasper St. Pierre <jstpierre@mecheye.net>
* Giovanni Campagna <scampa.giovanni@gmail.com>
*/
#include <string.h>
#define XP_UNIX 1
#include "npapi/npapi.h"
#include "npapi/npruntime.h"
#include "npapi/npfunctions.h"
#include <glib.h>
#include <gio/gio.h>
#include <json-glib/json-glib.h>
#define ORIGIN "extensions.gnome.org"
#define PLUGIN_NAME "Gnome Shell Integration"
#define PLUGIN_DESCRIPTION "This plugin provides integration with Gnome Shell " \
"for live extension enabling and disabling. " \
"It can be used only by extensions.gnome.org"
#define PLUGIN_MIME_STRING "application/x-gnome-shell-integration::Gnome Shell Integration Dummy Content-Type";
#define PLUGIN_API_VERSION 3
typedef struct {
GDBusProxy *proxy;
} PluginData;
static NPNetscapeFuncs funcs;
static inline gchar *
get_string_property (NPP instance,
NPObject *obj,
const char *name)
{
NPVariant result = { NPVariantType_Void };
NPString result_str;
gchar *result_copy;
result_copy = NULL;
if (!funcs.getproperty (instance, obj,
funcs.getstringidentifier (name),
&result))
goto out;
if (!NPVARIANT_IS_STRING (result))
goto out;
result_str = NPVARIANT_TO_STRING (result);
result_copy = g_strndup (result_str.UTF8Characters, result_str.UTF8Length);
out:
funcs.releasevariantvalue (&result);
return result_copy;
}
static gboolean
check_origin_and_protocol (NPP instance)
{
gboolean ret = FALSE;
NPError error;
NPObject *window = NULL;
NPVariant document = { NPVariantType_Void };
NPVariant location = { NPVariantType_Void };
gchar *hostname = NULL;
gchar *protocol = NULL;
error = funcs.getvalue (instance, NPNVWindowNPObject, &window);
if (error != NPERR_NO_ERROR)
goto out;
if (!funcs.getproperty (instance, window,
funcs.getstringidentifier ("document"),
&document))
goto out;
if (!NPVARIANT_IS_OBJECT (document))
goto out;
if (!funcs.getproperty (instance, NPVARIANT_TO_OBJECT (document),
funcs.getstringidentifier ("location"),
&location))
goto out;
if (!NPVARIANT_IS_OBJECT (document))
goto out;
hostname = get_string_property (instance,
NPVARIANT_TO_OBJECT (location),
"hostname");
if (g_strcmp0 (hostname, ORIGIN))
{
g_debug ("origin does not match, is %s",
hostname);
goto out;
}
protocol = get_string_property (instance,
NPVARIANT_TO_OBJECT (location),
"protocol");
if (g_strcmp0 (protocol, "https:") != 0)
{
g_debug ("protocol does not match, is %s",
protocol);
goto out;
}
ret = TRUE;
out:
g_free (protocol);
g_free (hostname);
funcs.releasevariantvalue (&location);
funcs.releasevariantvalue (&document);
if (window != NULL)
funcs.releaseobject (window);
return ret;
}
/* =============== public entry points =================== */
NPError
NP_Initialize(NPNetscapeFuncs *pfuncs, NPPluginFuncs *plugin)
{
/* global initialization routine, called once when plugin
is loaded */
g_debug ("plugin loaded");
memcpy (&funcs, pfuncs, sizeof (funcs));
plugin->size = sizeof(NPPluginFuncs);
plugin->newp = NPP_New;
plugin->destroy = NPP_Destroy;
plugin->getvalue = NPP_GetValue;
return NPERR_NO_ERROR;
}
NPError
NP_Shutdown(void)
{
return NPERR_NO_ERROR;
}
const char*
NP_GetMIMEDescription(void)
{
return PLUGIN_MIME_STRING;
}
NPError
NP_GetValue(void *instance,
NPPVariable variable,
void *value)
{
switch (variable) {
case NPPVpluginNameString:
*(char**)value = PLUGIN_NAME;
break;
case NPPVpluginDescriptionString:
*(char**)value = PLUGIN_DESCRIPTION;
break;
default:
;
}
return NPERR_NO_ERROR;
}
NPError
NPP_New(NPMIMEType mimetype,
NPP instance,
uint16_t mode,
int16_t argc,
char **argn,
char **argv,
NPSavedData *saved)
{
/* instance initialization function */
PluginData *data;
GError *error = NULL;
g_debug ("plugin created");
if (!check_origin_and_protocol (instance))
return NPERR_GENERIC_ERROR;
data = g_slice_new (PluginData);
instance->pdata = data;
data->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
G_DBUS_PROXY_FLAGS_NONE,
NULL, /* interface info */
"org.gnome.Shell",
"/org/gnome/Shell",
"org.gnome.Shell",
NULL, /* GCancellable */
&error);
if (!data->proxy)
{
/* ignore error if the shell is not running, otherwise warn */
if (error->domain != G_DBUS_ERROR ||
error->code != G_DBUS_ERROR_NAME_HAS_NO_OWNER)
{
g_warning ("Failed to set up Shell proxy: %s", error->message);
}
g_clear_error (&error);
return NPERR_GENERIC_ERROR;
}
g_debug ("plugin created successfully");
return NPERR_NO_ERROR;
}
NPError
NPP_Destroy(NPP instance,
NPSavedData **saved)
{
/* instance finalization function */
PluginData *data = instance->pdata;
g_debug ("plugin destroyed");
g_object_unref (data->proxy);
g_slice_free (PluginData, data);
return NPERR_NO_ERROR;
}
/* =================== scripting interface =================== */
typedef struct {
NPObject parent;
NPP instance;
GDBusProxy *proxy;
NPObject *listener;
NPObject *restart_listener;
gint signal_id;
guint watch_name_id;
} PluginObject;
static void
on_shell_signal (GDBusProxy *proxy,
gchar *sender_name,
gchar *signal_name,
GVariant *parameters,
gpointer user_data)
{
PluginObject *obj = user_data;
if (strcmp (signal_name, "ExtensionStatusChanged") == 0)
{
gchar *uuid;
gint32 status;
gchar *error;
NPVariant args[3];
NPVariant result = { NPVariantType_Void };
g_variant_get (parameters, "(sis)", &uuid, &status, &error);
STRINGZ_TO_NPVARIANT (uuid, args[0]);
INT32_TO_NPVARIANT (status, args[1]);
STRINGZ_TO_NPVARIANT (error, args[2]);
funcs.invokeDefault (obj->instance, obj->listener,
args, 3, &result);
funcs.releasevariantvalue (&result);
g_free (uuid);
g_free (error);
}
}
static void
on_shell_appeared (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
gpointer user_data)
{
PluginObject *obj = (PluginObject*) user_data;
if (obj->restart_listener)
{
NPVariant result = { NPVariantType_Void };
funcs.invokeDefault (obj->instance, obj->restart_listener,
NULL, 0, &result);
funcs.releasevariantvalue (&result);
}
}
static NPObject *
plugin_object_allocate (NPP instance,
NPClass *klass)
{
PluginData *data = instance->pdata;
PluginObject *obj = g_slice_new0 (PluginObject);
obj->instance = instance;
obj->proxy = g_object_ref (data->proxy);
obj->signal_id = g_signal_connect (obj->proxy, "g-signal",
G_CALLBACK (on_shell_signal), obj);
obj->watch_name_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
"org.gnome.Shell",
G_BUS_NAME_WATCHER_FLAGS_NONE,
on_shell_appeared,
NULL,
obj,
NULL);
g_debug ("plugin object created");
return (NPObject*)obj;
}
static void
plugin_object_deallocate (NPObject *npobj)
{
PluginObject *obj = (PluginObject*)npobj;
g_signal_handler_disconnect (obj->proxy, obj->signal_id);
g_object_unref (obj->proxy);
if (obj->listener)
funcs.releaseobject (obj->listener);
if (obj->watch_name_id)
g_bus_unwatch_name (obj->watch_name_id);
g_debug ("plugin object destroyed");
g_slice_free (PluginObject, obj);
}
static NPIdentifier api_version_id;
static NPIdentifier shell_version_id;
static NPIdentifier get_info_id;
static NPIdentifier list_extensions_id;
static NPIdentifier enable_extension_id;
static NPIdentifier install_extension_id;
static NPIdentifier uninstall_extension_id;
static NPIdentifier onextension_changed_id;
static NPIdentifier onrestart_id;
static NPIdentifier get_errors_id;
static NPIdentifier launch_extension_prefs_id;
static bool
plugin_object_has_method (NPObject *npobj,
NPIdentifier name)
{
return (name == get_info_id ||
name == list_extensions_id ||
name == enable_extension_id ||
name == install_extension_id ||
name == uninstall_extension_id ||
name == get_errors_id ||
name == launch_extension_prefs_id);
}
static inline gboolean
uuid_is_valid (const gchar *uuid)
{
gsize i;
for (i = 0; uuid[i]; i ++)
{
gchar c = uuid[i];
if (c < 32 || c >= 127)
return FALSE;
switch (c)
{
case '&':
case '<':
case '>':
case '/':
case '\\':
return FALSE;
default:
break;
}
}
return TRUE;
}
static gboolean
jsonify_variant (GVariant *variant,
NPVariant *result)
{
gboolean ret;
GVariant *real_value;
JsonNode *root;
JsonGenerator *generator;
gsize json_length;
gchar *json;
gchar *buffer;
ret = TRUE;
/* DBus methods can return multiple values,
* but we're only interested in the first. */
g_variant_get (variant, "(@*)", &real_value);
root = json_gvariant_serialize (real_value);
generator = json_generator_new ();
json_generator_set_root (generator, root);
json = json_generator_to_data (generator, &json_length);
buffer = funcs.memalloc (json_length + 1);
if (!buffer)
{
ret = FALSE;
goto out;
}
strcpy (buffer, json);
STRINGN_TO_NPVARIANT (buffer, json_length, *result);
out:
g_variant_unref (variant);
g_variant_unref (real_value);
json_node_free (root);
g_free (json);
return ret;
}
static gboolean
plugin_list_extensions (PluginObject *obj,
NPVariant *result)
{
GError *error = NULL;
GVariant *res;
res = g_dbus_proxy_call_sync (obj->proxy,
"ListExtensions",
NULL, /* parameters */
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
NULL, /* cancellable */
&error);
if (!res)
{
g_warning ("Failed to retrieve extension list: %s", error->message);
g_error_free (error);
return FALSE;
}
return jsonify_variant (res, result);
}
static gboolean
plugin_enable_extension (PluginObject *obj,
NPString uuid,
gboolean enabled)
{
gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
return FALSE;
}
g_dbus_proxy_call (obj->proxy,
(enabled ? "EnableExtension" : "DisableExtension"),
g_variant_new ("(s)", uuid_str),
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
NULL, /* cancellable */
NULL, /* callback */
NULL /* user_data */);
g_free (uuid_str);
return TRUE;
}
static gboolean
plugin_install_extension (PluginObject *obj,
NPString uuid,
NPString version_tag)
{
gchar *uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
gchar *version_tag_str;
if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
return FALSE;
}
version_tag_str = g_strndup (version_tag.UTF8Characters,
version_tag.UTF8Length);
g_dbus_proxy_call (obj->proxy,
"InstallRemoteExtension",
g_variant_new ("(ss)",
uuid_str,
version_tag_str),
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
NULL, /* cancellable */
NULL, /* callback */
NULL /* user_data */);
g_free (uuid_str);
g_free (version_tag_str);
return TRUE;
}
static gboolean
plugin_uninstall_extension (PluginObject *obj,
NPString uuid,
NPVariant *result)
{
GError *error = NULL;
GVariant *res;
gchar *uuid_str;
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
return FALSE;
}
res = g_dbus_proxy_call_sync (obj->proxy,
"UninstallExtension",
g_variant_new ("(s)",
uuid_str),
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
NULL, /* cancellable */
&error);
g_free (uuid_str);
if (!res)
{
g_warning ("Failed to uninstall extension: %s", error->message);
g_error_free (error);
return FALSE;
}
return jsonify_variant (res, result);
}
static gboolean
plugin_get_info (PluginObject *obj,
NPString uuid,
NPVariant *result)
{
GError *error = NULL;
GVariant *res;
gchar *uuid_str;
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
return FALSE;
}
res = g_dbus_proxy_call_sync (obj->proxy,
"GetExtensionInfo",
g_variant_new ("(s)", uuid_str),
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
NULL, /* cancellable */
&error);
g_free (uuid_str);
if (!res)
{
g_warning ("Failed to retrieve extension metadata: %s", error->message);
g_error_free (error);
return FALSE;
}
return jsonify_variant (res, result);
}
static gboolean
plugin_get_errors (PluginObject *obj,
NPString uuid,
NPVariant *result)
{
GError *error = NULL;
GVariant *res;
gchar *uuid_str;
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
return FALSE;
}
res = g_dbus_proxy_call_sync (obj->proxy,
"GetExtensionErrors",
g_variant_new ("(s)", uuid_str),
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
NULL, /* cancellable */
&error);
g_free (uuid_str);
if (!res)
{
g_warning ("Failed to retrieve errors: %s", error->message);
g_error_free (error);
return FALSE;
}
return jsonify_variant (res, result);
}
static gboolean
plugin_launch_extension_prefs (PluginObject *obj,
NPString uuid,
NPVariant *result)
{
gchar *uuid_str;
uuid_str = g_strndup (uuid.UTF8Characters, uuid.UTF8Length);
if (!uuid_is_valid (uuid_str))
{
g_free (uuid_str);
return FALSE;
}
g_dbus_proxy_call (obj->proxy,
"LaunchExtensionPrefs",
g_variant_new ("(s)", uuid_str),
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
NULL, /* cancellable */
NULL, /* callback */
NULL /* user_data */);
g_free (uuid_str);
return TRUE;
}
static int
plugin_get_api_version (PluginObject *obj,
NPVariant *result)
{
INT32_TO_NPVARIANT (PLUGIN_API_VERSION, *result);
return TRUE;
}
static gboolean
plugin_get_shell_version (PluginObject *obj,
NPVariant *result)
{
GVariant *res;
const gchar *version;
gsize length;
gchar *buffer;
gboolean ret;
ret = TRUE;
res = g_dbus_proxy_get_cached_property (obj->proxy,
"ShellVersion");
if (res == NULL)
{
g_warning ("Failed to grab shell version.");
version = "-1";
}
else
{
g_variant_get (res, "&s", &version);
}
length = strlen (version);
buffer = funcs.memalloc (length + 1);
if (!buffer)
{
ret = FALSE;
goto out;
}
strcpy (buffer, version);
STRINGN_TO_NPVARIANT (buffer, length, *result);
out:
if (res)
g_variant_unref (res);
return ret;
}
static bool
plugin_object_invoke (NPObject *npobj,
NPIdentifier name,
const NPVariant *args,
uint32_t argc,
NPVariant *result)
{
PluginObject *obj;
g_debug ("invoking plugin object method");
obj = (PluginObject*) npobj;
VOID_TO_NPVARIANT (*result);
if (!plugin_object_has_method (npobj, name))
return FALSE;
if (name == list_extensions_id)
return plugin_list_extensions (obj, result);
else if (name == get_info_id)
{
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
return plugin_get_info (obj, NPVARIANT_TO_STRING(args[0]), result);
}
else if (name == enable_extension_id)
{
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
if (!NPVARIANT_IS_BOOLEAN(args[1])) return FALSE;
return plugin_enable_extension (obj,
NPVARIANT_TO_STRING(args[0]),
NPVARIANT_TO_BOOLEAN(args[1]));
}
else if (name == install_extension_id)
{
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
if (!NPVARIANT_IS_STRING(args[1])) return FALSE;
return plugin_install_extension (obj,
NPVARIANT_TO_STRING(args[0]),
NPVARIANT_TO_STRING(args[1]));
}
else if (name == uninstall_extension_id)
{
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
return plugin_uninstall_extension (obj,
NPVARIANT_TO_STRING(args[0]),
result);
}
else if (name == get_errors_id)
{
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
return plugin_get_errors (obj,
NPVARIANT_TO_STRING(args[0]),
result);
}
else if (name == launch_extension_prefs_id)
{
if (!NPVARIANT_IS_STRING(args[0])) return FALSE;
return plugin_launch_extension_prefs (obj,
NPVARIANT_TO_STRING(args[0]),
result);
}
return TRUE;
}
static bool
plugin_object_has_property (NPObject *npobj,
NPIdentifier name)
{
return (name == onextension_changed_id ||
name == onrestart_id ||
name == api_version_id ||
name == shell_version_id);
}
static bool
plugin_object_get_property (NPObject *npobj,
NPIdentifier name,
NPVariant *result)
{
PluginObject *obj;
if (!plugin_object_has_property (npobj, name))
return FALSE;
obj = (PluginObject*) npobj;
if (name == api_version_id)
return plugin_get_api_version (obj, result);
else if (name == shell_version_id)
return plugin_get_shell_version (obj, result);
else if (name == onextension_changed_id)
{
if (obj->listener)
OBJECT_TO_NPVARIANT (obj->listener, *result);
else
NULL_TO_NPVARIANT (*result);
}
else if (name == onrestart_id)
{
if (obj->restart_listener)
OBJECT_TO_NPVARIANT (obj->restart_listener, *result);
else
NULL_TO_NPVARIANT (*result);
}
return TRUE;
}
static bool
plugin_object_set_callback (NPObject **listener,
const NPVariant *value)
{
if (!NPVARIANT_IS_OBJECT (*value) && !NPVARIANT_IS_NULL (*value))
return FALSE;
if (*listener)
funcs.releaseobject (*listener);
*listener = NULL;
if (NPVARIANT_IS_OBJECT (*value))
{
*listener = NPVARIANT_TO_OBJECT (*value);
funcs.retainobject (*listener);
}
return TRUE;
}
static bool
plugin_object_set_property (NPObject *npobj,
NPIdentifier name,
const NPVariant *value)
{
PluginObject *obj;
obj = (PluginObject *)npobj;
if (name == onextension_changed_id)
return plugin_object_set_callback (&obj->listener, value);
if (name == onrestart_id)
return plugin_object_set_callback (&obj->restart_listener, value);
return FALSE;
}
static NPClass plugin_class = {
NP_CLASS_STRUCT_VERSION,
plugin_object_allocate,
plugin_object_deallocate,
NULL, /* invalidate */
plugin_object_has_method,
plugin_object_invoke,
NULL, /* invoke default */
plugin_object_has_property,
plugin_object_get_property,
plugin_object_set_property,
NULL, /* remove property */
NULL, /* enumerate */
NULL, /* construct */
};
static void
init_methods_and_properties (void)
{
/* this is the JS public API; it is manipulated through NPIdentifiers for speed */
api_version_id = funcs.getstringidentifier ("apiVersion");
shell_version_id = funcs.getstringidentifier ("shellVersion");
get_info_id = funcs.getstringidentifier ("getExtensionInfo");
list_extensions_id = funcs.getstringidentifier ("listExtensions");
enable_extension_id = funcs.getstringidentifier ("setExtensionEnabled");
install_extension_id = funcs.getstringidentifier ("installExtension");
uninstall_extension_id = funcs.getstringidentifier ("uninstallExtension");
get_errors_id = funcs.getstringidentifier ("getExtensionErrors");
launch_extension_prefs_id = funcs.getstringidentifier ("launchExtensionPrefs");
onrestart_id = funcs.getstringidentifier ("onshellrestart");
onextension_changed_id = funcs.getstringidentifier ("onchange");
}
NPError
NPP_GetValue(NPP instance,
NPPVariable variable,
void *value)
{
g_debug ("NPP_GetValue called");
switch (variable) {
case NPPVpluginScriptableNPObject:
g_debug ("creating scriptable object");
init_methods_and_properties ();
*(NPObject**)value = funcs.createobject (instance, &plugin_class);
break;
case NPPVpluginNeedsXEmbed:
*(bool *)value = TRUE;
break;
default:
;
}
return NPERR_NO_ERROR;
}

View File

@ -0,0 +1,893 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef npapi_h_
#define npapi_h_
#if defined(__OS2__)
#pragma pack(1)
#endif
#include "nptypes.h"
#if defined(__OS2__) || defined(OS2)
#ifndef XP_OS2
#define XP_OS2 1
#endif
#endif
#if defined(_WIN32) && !defined(__SYMBIAN32__)
#include <windef.h>
#ifndef XP_WIN
#define XP_WIN 1
#endif
#endif
#if defined(__SYMBIAN32__)
#ifndef XP_SYMBIAN
#define XP_SYMBIAN 1
#undef XP_WIN
#endif
#endif
#if defined(__APPLE_CC__) && !defined(XP_UNIX)
#ifndef XP_MACOSX
#define XP_MACOSX 1
#endif
#endif
#if defined(XP_MACOSX) && defined(__LP64__)
#define NP_NO_QUICKDRAW
#define NP_NO_CARBON
#endif
#if defined(XP_MACOSX)
#include <ApplicationServices/ApplicationServices.h>
#include <OpenGL/OpenGL.h>
#ifndef NP_NO_CARBON
#include <Carbon/Carbon.h>
#endif
#endif
#if defined(XP_UNIX)
#include <stdio.h>
#if defined(MOZ_X11)
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#endif
#endif
#if defined(XP_SYMBIAN)
#include <QEvent>
#include <QRegion>
#endif
/*----------------------------------------------------------------------*/
/* Plugin Version Constants */
/*----------------------------------------------------------------------*/
#define NP_VERSION_MAJOR 0
#define NP_VERSION_MINOR 27
/* The OS/2 version of Netscape uses RC_DATA to define the
mime types, file extensions, etc that are required.
Use a vertical bar to separate types, end types with \0.
FileVersion and ProductVersion are 32bit ints, all other
entries are strings that MUST be terminated with a \0.
AN EXAMPLE:
RCDATA NP_INFO_ProductVersion { 1,0,0,1,}
RCDATA NP_INFO_MIMEType { "video/x-video|",
"video/x-flick\0" }
RCDATA NP_INFO_FileExtents { "avi|",
"flc\0" }
RCDATA NP_INFO_FileOpenName{ "MMOS2 video player(*.avi)|",
"MMOS2 Flc/Fli player(*.flc)\0" }
RCDATA NP_INFO_FileVersion { 1,0,0,1 }
RCDATA NP_INFO_CompanyName { "Netscape Communications\0" }
RCDATA NP_INFO_FileDescription { "NPAVI32 Extension DLL\0"
RCDATA NP_INFO_InternalName { "NPAVI32\0" )
RCDATA NP_INFO_LegalCopyright { "Copyright Netscape Communications \251 1996\0"
RCDATA NP_INFO_OriginalFilename { "NVAPI32.DLL" }
RCDATA NP_INFO_ProductName { "NPAVI32 Dynamic Link Library\0" }
*/
/* RC_DATA types for version info - required */
#define NP_INFO_ProductVersion 1
#define NP_INFO_MIMEType 2
#define NP_INFO_FileOpenName 3
#define NP_INFO_FileExtents 4
/* RC_DATA types for version info - used if found */
#define NP_INFO_FileDescription 5
#define NP_INFO_ProductName 6
/* RC_DATA types for version info - optional */
#define NP_INFO_CompanyName 7
#define NP_INFO_FileVersion 8
#define NP_INFO_InternalName 9
#define NP_INFO_LegalCopyright 10
#define NP_INFO_OriginalFilename 11
#ifndef RC_INVOKED
/*----------------------------------------------------------------------*/
/* Definition of Basic Types */
/*----------------------------------------------------------------------*/
typedef unsigned char NPBool;
typedef int16_t NPError;
typedef int16_t NPReason;
typedef char* NPMIMEType;
/*----------------------------------------------------------------------*/
/* Structures and definitions */
/*----------------------------------------------------------------------*/
#if !defined(__LP64__)
#if defined(XP_MACOSX)
#pragma options align=mac68k
#endif
#endif /* __LP64__ */
/*
* NPP is a plug-in's opaque instance handle
*/
typedef struct _NPP
{
void* pdata; /* plug-in private data */
void* ndata; /* netscape private data */
} NPP_t;
typedef NPP_t* NPP;
typedef struct _NPStream
{
void* pdata; /* plug-in private data */
void* ndata; /* netscape private data */
const char* url;
uint32_t end;
uint32_t lastmodified;
void* notifyData;
const char* headers; /* Response headers from host.
* Exists only for >= NPVERS_HAS_RESPONSE_HEADERS.
* Used for HTTP only; NULL for non-HTTP.
* Available from NPP_NewStream onwards.
* Plugin should copy this data before storing it.
* Includes HTTP status line and all headers,
* preferably verbatim as received from server,
* headers formatted as in HTTP ("Header: Value"),
* and newlines (\n, NOT \r\n) separating lines.
* Terminated by \n\0 (NOT \n\n\0). */
} NPStream;
typedef struct _NPByteRange
{
int32_t offset; /* negative offset means from the end */
uint32_t length;
struct _NPByteRange* next;
} NPByteRange;
typedef struct _NPSavedData
{
int32_t len;
void* buf;
} NPSavedData;
typedef struct _NPRect
{
uint16_t top;
uint16_t left;
uint16_t bottom;
uint16_t right;
} NPRect;
typedef struct _NPSize
{
int32_t width;
int32_t height;
} NPSize;
typedef enum {
NPFocusNext = 0,
NPFocusPrevious = 1
} NPFocusDirection;
/* Return values for NPP_HandleEvent */
#define kNPEventNotHandled 0
#define kNPEventHandled 1
/* Exact meaning must be spec'd in event model. */
#define kNPEventStartIME 2
#if defined(XP_UNIX)
/*
* Unix specific structures and definitions
*/
/*
* Callback Structures.
*
* These are used to pass additional platform specific information.
*/
enum {
NP_SETWINDOW = 1,
NP_PRINT
};
typedef struct
{
int32_t type;
} NPAnyCallbackStruct;
typedef struct
{
int32_t type;
#if defined(MOZ_X11)
Display* display;
Visual* visual;
Colormap colormap;
unsigned int depth;
#endif
} NPSetWindowCallbackStruct;
typedef struct
{
int32_t type;
FILE* fp;
} NPPrintCallbackStruct;
#endif /* XP_UNIX */
#if defined(XP_MACOSX)
typedef enum {
#ifndef NP_NO_QUICKDRAW
NPDrawingModelQuickDraw = 0,
#endif
NPDrawingModelCoreGraphics = 1,
NPDrawingModelOpenGL = 2,
NPDrawingModelCoreAnimation = 3,
NPDrawingModelInvalidatingCoreAnimation = 4
} NPDrawingModel;
typedef enum {
#ifndef NP_NO_CARBON
NPEventModelCarbon = 0,
#endif
NPEventModelCocoa = 1
} NPEventModel;
#endif
/*
* The following masks are applied on certain platforms to NPNV and
* NPPV selectors that pass around pointers to COM interfaces. Newer
* compilers on some platforms may generate vtables that are not
* compatible with older compilers. To prevent older plugins from
* not understanding a new browser's ABI, these masks change the
* values of those selectors on those platforms. To remain backwards
* compatible with different versions of the browser, plugins can
* use these masks to dynamically determine and use the correct C++
* ABI that the browser is expecting. This does not apply to Windows
* as Microsoft's COM ABI will likely not change.
*/
#define NP_ABI_GCC3_MASK 0x10000000
/*
* gcc 3.x generated vtables on UNIX and OSX are incompatible with
* previous compilers.
*/
#if (defined(XP_UNIX) && defined(__GNUC__) && (__GNUC__ >= 3))
#define _NP_ABI_MIXIN_FOR_GCC3 NP_ABI_GCC3_MASK
#else
#define _NP_ABI_MIXIN_FOR_GCC3 0
#endif
#if defined(XP_MACOSX)
#define NP_ABI_MACHO_MASK 0x01000000
#define _NP_ABI_MIXIN_FOR_MACHO NP_ABI_MACHO_MASK
#else
#define _NP_ABI_MIXIN_FOR_MACHO 0
#endif
#define NP_ABI_MASK (_NP_ABI_MIXIN_FOR_GCC3 | _NP_ABI_MIXIN_FOR_MACHO)
/*
* List of variable names for which NPP_GetValue shall be implemented
*/
typedef enum {
NPPVpluginNameString = 1,
NPPVpluginDescriptionString,
NPPVpluginWindowBool,
NPPVpluginTransparentBool,
NPPVjavaClass,
NPPVpluginWindowSize,
NPPVpluginTimerInterval,
NPPVpluginScriptableInstance = (10 | NP_ABI_MASK),
NPPVpluginScriptableIID = 11,
NPPVjavascriptPushCallerBool = 12,
NPPVpluginKeepLibraryInMemory = 13,
NPPVpluginNeedsXEmbed = 14,
/* Get the NPObject for scripting the plugin. Introduced in NPAPI minor version 14.
*/
NPPVpluginScriptableNPObject = 15,
/* Get the plugin value (as \0-terminated UTF-8 string data) for
* form submission if the plugin is part of a form. Use
* NPN_MemAlloc() to allocate memory for the string data. Introduced
* in NPAPI minor version 15.
*/
NPPVformValue = 16,
NPPVpluginUrlRequestsDisplayedBool = 17,
/* Checks if the plugin is interested in receiving the http body of
* all http requests (including failed ones, http status != 200).
*/
NPPVpluginWantsAllNetworkStreams = 18,
/* Browsers can retrieve a native ATK accessibility plug ID via this variable. */
NPPVpluginNativeAccessibleAtkPlugId = 19,
/* Checks to see if the plug-in would like the browser to load the "src" attribute. */
NPPVpluginCancelSrcStream = 20,
NPPVsupportsAdvancedKeyHandling = 21,
NPPVpluginUsesDOMForCursorBool = 22
#if defined(XP_MACOSX)
/* Used for negotiating drawing models */
, NPPVpluginDrawingModel = 1000
/* Used for negotiating event models */
, NPPVpluginEventModel = 1001
/* In the NPDrawingModelCoreAnimation drawing model, the browser asks the plug-in for a Core Animation layer. */
, NPPVpluginCoreAnimationLayer = 1003
#endif
#if (MOZ_PLATFORM_MAEMO == 5) || (MOZ_PLATFORM_MAEMO == 6)
, NPPVpluginWindowlessLocalBool = 2002
#endif
} NPPVariable;
/*
* List of variable names for which NPN_GetValue should be implemented.
*/
typedef enum {
NPNVxDisplay = 1,
NPNVxtAppContext,
NPNVnetscapeWindow,
NPNVjavascriptEnabledBool,
NPNVasdEnabledBool,
NPNVisOfflineBool,
NPNVserviceManager = (10 | NP_ABI_MASK),
NPNVDOMElement = (11 | NP_ABI_MASK),
NPNVDOMWindow = (12 | NP_ABI_MASK),
NPNVToolkit = (13 | NP_ABI_MASK),
NPNVSupportsXEmbedBool = 14,
/* Get the NPObject wrapper for the browser window. */
NPNVWindowNPObject = 15,
/* Get the NPObject wrapper for the plugins DOM element. */
NPNVPluginElementNPObject = 16,
NPNVSupportsWindowless = 17,
NPNVprivateModeBool = 18,
NPNVsupportsAdvancedKeyHandling = 21
#if defined(XP_MACOSX)
/* Used for negotiating drawing models */
, NPNVpluginDrawingModel = 1000
#ifndef NP_NO_QUICKDRAW
, NPNVsupportsQuickDrawBool = 2000
#endif
, NPNVsupportsCoreGraphicsBool = 2001
, NPNVsupportsOpenGLBool = 2002
, NPNVsupportsCoreAnimationBool = 2003
, NPNVsupportsInvalidatingCoreAnimationBool = 2004
#ifndef NP_NO_CARBON
, NPNVsupportsCarbonBool = 3000 /* TRUE if the browser supports the Carbon event model */
#endif
, NPNVsupportsCocoaBool = 3001 /* TRUE if the browser supports the Cocoa event model */
, NPNVsupportsUpdatedCocoaTextInputBool = 3002 /* TRUE if the browser supports the updated
Cocoa text input specification. */
, NPNVsupportsCompositingCoreAnimationPluginsBool = 74656 /* TRUE if the browser supports
CA model compositing */
#endif
#if (MOZ_PLATFORM_MAEMO == 5) || (MOZ_PLATFORM_MAEMO == 6)
, NPNVSupportsWindowlessLocal = 2002
#endif
} NPNVariable;
typedef enum {
NPNURLVCookie = 501,
NPNURLVProxy
} NPNURLVariable;
/*
* The type of Toolkit the widgets use
*/
typedef enum {
NPNVGtk12 = 1,
NPNVGtk2
} NPNToolkitType;
/*
* The type of a NPWindow - it specifies the type of the data structure
* returned in the window field.
*/
typedef enum {
NPWindowTypeWindow = 1,
NPWindowTypeDrawable
} NPWindowType;
typedef struct _NPWindow
{
void* window; /* Platform specific window handle */
/* OS/2: x - Position of bottom left corner */
/* OS/2: y - relative to visible netscape window */
int32_t x; /* Position of top left corner relative */
int32_t y; /* to a netscape page. */
uint32_t width; /* Maximum window size */
uint32_t height;
NPRect clipRect; /* Clipping rectangle in port coordinates */
#if (defined(XP_UNIX) || defined(XP_SYMBIAN)) && !defined(XP_MACOSX)
void * ws_info; /* Platform-dependent additional data */
#endif /* XP_UNIX */
NPWindowType type; /* Is this a window or a drawable? */
} NPWindow;
typedef struct _NPImageExpose
{
char* data; /* image pointer */
int32_t stride; /* Stride of data image pointer */
int32_t depth; /* Depth of image pointer */
int32_t x; /* Expose x */
int32_t y; /* Expose y */
uint32_t width; /* Expose width */
uint32_t height; /* Expose height */
NPSize dataSize; /* Data buffer size */
float translateX; /* translate X matrix value */
float translateY; /* translate Y matrix value */
float scaleX; /* scale X matrix value */
float scaleY; /* scale Y matrix value */
} NPImageExpose;
typedef struct _NPFullPrint
{
NPBool pluginPrinted;/* Set TRUE if plugin handled fullscreen printing */
NPBool printOne; /* TRUE if plugin should print one copy to default
printer */
void* platformPrint; /* Platform-specific printing info */
} NPFullPrint;
typedef struct _NPEmbedPrint
{
NPWindow window;
void* platformPrint; /* Platform-specific printing info */
} NPEmbedPrint;
typedef struct _NPPrint
{
uint16_t mode; /* NP_FULL or NP_EMBED */
union
{
NPFullPrint fullPrint; /* if mode is NP_FULL */
NPEmbedPrint embedPrint; /* if mode is NP_EMBED */
} print;
} NPPrint;
#if defined(XP_MACOSX)
#ifndef NP_NO_CARBON
typedef EventRecord NPEvent;
#endif
#elif defined(XP_SYMBIAN)
typedef QEvent NPEvent;
#elif defined(XP_WIN)
typedef struct _NPEvent
{
uint16_t event;
uintptr_t wParam;
uintptr_t lParam;
} NPEvent;
#elif defined(XP_OS2)
typedef struct _NPEvent
{
uint32_t event;
uint32_t wParam;
uint32_t lParam;
} NPEvent;
#elif defined(XP_UNIX) && defined(MOZ_X11)
typedef XEvent NPEvent;
#else
typedef void* NPEvent;
#endif
#if defined(XP_MACOSX)
typedef void* NPRegion;
#ifndef NP_NO_QUICKDRAW
typedef RgnHandle NPQDRegion;
#endif
typedef CGPathRef NPCGRegion;
#elif defined(XP_WIN)
typedef HRGN NPRegion;
#elif defined(XP_UNIX) && defined(MOZ_X11)
typedef Region NPRegion;
#elif defined(XP_SYMBIAN)
typedef QRegion* NPRegion;
#else
typedef void *NPRegion;
#endif
typedef struct _NPNSString NPNSString;
typedef struct _NPNSWindow NPNSWindow;
typedef struct _NPNSMenu NPNSMenu;
#if defined(XP_MACOSX)
typedef NPNSMenu NPMenu;
#else
typedef void *NPMenu;
#endif
typedef enum {
NPCoordinateSpacePlugin = 1,
NPCoordinateSpaceWindow,
NPCoordinateSpaceFlippedWindow,
NPCoordinateSpaceScreen,
NPCoordinateSpaceFlippedScreen
} NPCoordinateSpace;
#if defined(XP_MACOSX)
#ifndef NP_NO_QUICKDRAW
typedef struct NP_Port
{
CGrafPtr port;
int32_t portx; /* position inside the topmost window */
int32_t porty;
} NP_Port;
#endif /* NP_NO_QUICKDRAW */
/*
* NP_CGContext is the type of the NPWindow's 'window' when the plugin specifies NPDrawingModelCoreGraphics
* as its drawing model.
*/
typedef struct NP_CGContext
{
CGContextRef context;
void *window; /* A WindowRef under the Carbon event model. */
} NP_CGContext;
/*
* NP_GLContext is the type of the NPWindow's 'window' when the plugin specifies NPDrawingModelOpenGL as its
* drawing model.
*/
typedef struct NP_GLContext
{
CGLContextObj context;
#ifdef NP_NO_CARBON
NPNSWindow *window;
#else
void *window; /* Can be either an NSWindow or a WindowRef depending on the event model */
#endif
} NP_GLContext;
typedef enum {
NPCocoaEventDrawRect = 1,
NPCocoaEventMouseDown,
NPCocoaEventMouseUp,
NPCocoaEventMouseMoved,
NPCocoaEventMouseEntered,
NPCocoaEventMouseExited,
NPCocoaEventMouseDragged,
NPCocoaEventKeyDown,
NPCocoaEventKeyUp,
NPCocoaEventFlagsChanged,
NPCocoaEventFocusChanged,
NPCocoaEventWindowFocusChanged,
NPCocoaEventScrollWheel,
NPCocoaEventTextInput
} NPCocoaEventType;
typedef struct _NPCocoaEvent {
NPCocoaEventType type;
uint32_t version;
union {
struct {
uint32_t modifierFlags;
double pluginX;
double pluginY;
int32_t buttonNumber;
int32_t clickCount;
double deltaX;
double deltaY;
double deltaZ;
} mouse;
struct {
uint32_t modifierFlags;
NPNSString *characters;
NPNSString *charactersIgnoringModifiers;
NPBool isARepeat;
uint16_t keyCode;
} key;
struct {
CGContextRef context;
double x;
double y;
double width;
double height;
} draw;
struct {
NPBool hasFocus;
} focus;
struct {
NPNSString *text;
} text;
} data;
} NPCocoaEvent;
#ifndef NP_NO_CARBON
/* Non-standard event types that can be passed to HandleEvent */
enum NPEventType {
NPEventType_GetFocusEvent = (osEvt + 16),
NPEventType_LoseFocusEvent,
NPEventType_AdjustCursorEvent,
NPEventType_MenuCommandEvent,
NPEventType_ClippingChangedEvent,
NPEventType_ScrollingBeginsEvent = 1000,
NPEventType_ScrollingEndsEvent
};
#endif /* NP_NO_CARBON */
#endif /* XP_MACOSX */
/*
* Values for mode passed to NPP_New:
*/
#define NP_EMBED 1
#define NP_FULL 2
/*
* Values for stream type passed to NPP_NewStream:
*/
#define NP_NORMAL 1
#define NP_SEEK 2
#define NP_ASFILE 3
#define NP_ASFILEONLY 4
#define NP_MAXREADY (((unsigned)(~0)<<1)>>1)
/*
* Flags for NPP_ClearSiteData.
*/
#define NP_CLEAR_ALL 0
#define NP_CLEAR_CACHE (1 << 0)
#if !defined(__LP64__)
#if defined(XP_MACOSX)
#pragma options align=reset
#endif
#endif /* __LP64__ */
/*----------------------------------------------------------------------*/
/* Error and Reason Code definitions */
/*----------------------------------------------------------------------*/
/*
* Values of type NPError:
*/
#define NPERR_BASE 0
#define NPERR_NO_ERROR (NPERR_BASE + 0)
#define NPERR_GENERIC_ERROR (NPERR_BASE + 1)
#define NPERR_INVALID_INSTANCE_ERROR (NPERR_BASE + 2)
#define NPERR_INVALID_FUNCTABLE_ERROR (NPERR_BASE + 3)
#define NPERR_MODULE_LOAD_FAILED_ERROR (NPERR_BASE + 4)
#define NPERR_OUT_OF_MEMORY_ERROR (NPERR_BASE + 5)
#define NPERR_INVALID_PLUGIN_ERROR (NPERR_BASE + 6)
#define NPERR_INVALID_PLUGIN_DIR_ERROR (NPERR_BASE + 7)
#define NPERR_INCOMPATIBLE_VERSION_ERROR (NPERR_BASE + 8)
#define NPERR_INVALID_PARAM (NPERR_BASE + 9)
#define NPERR_INVALID_URL (NPERR_BASE + 10)
#define NPERR_FILE_NOT_FOUND (NPERR_BASE + 11)
#define NPERR_NO_DATA (NPERR_BASE + 12)
#define NPERR_STREAM_NOT_SEEKABLE (NPERR_BASE + 13)
#define NPERR_TIME_RANGE_NOT_SUPPORTED (NPERR_BASE + 14)
#define NPERR_MALFORMED_SITE (NPERR_BASE + 15)
/*
* Values of type NPReason:
*/
#define NPRES_BASE 0
#define NPRES_DONE (NPRES_BASE + 0)
#define NPRES_NETWORK_ERR (NPRES_BASE + 1)
#define NPRES_USER_BREAK (NPRES_BASE + 2)
/*
* Don't use these obsolete error codes any more.
*/
#define NP_NOERR NP_NOERR_is_obsolete_use_NPERR_NO_ERROR
#define NP_EINVAL NP_EINVAL_is_obsolete_use_NPERR_GENERIC_ERROR
#define NP_EABORT NP_EABORT_is_obsolete_use_NPRES_USER_BREAK
/*
* Version feature information
*/
#define NPVERS_HAS_STREAMOUTPUT 8
#define NPVERS_HAS_NOTIFICATION 9
#define NPVERS_HAS_LIVECONNECT 9
#define NPVERS_68K_HAS_LIVECONNECT 11
#define NPVERS_HAS_WINDOWLESS 11
#define NPVERS_HAS_XPCONNECT_SCRIPTING 13
#define NPVERS_HAS_NPRUNTIME_SCRIPTING 14
#define NPVERS_HAS_FORM_VALUES 15
#define NPVERS_HAS_POPUPS_ENABLED_STATE 16
#define NPVERS_HAS_RESPONSE_HEADERS 17
#define NPVERS_HAS_NPOBJECT_ENUM 18
#define NPVERS_HAS_PLUGIN_THREAD_ASYNC_CALL 19
#define NPVERS_HAS_ALL_NETWORK_STREAMS 20
#define NPVERS_HAS_URL_AND_AUTH_INFO 21
#define NPVERS_HAS_PRIVATE_MODE 22
#define NPVERS_MACOSX_HAS_COCOA_EVENTS 23
#define NPVERS_HAS_ADVANCED_KEY_HANDLING 25
#define NPVERS_HAS_URL_REDIRECT_HANDLING 26
#define NPVERS_HAS_CLEAR_SITE_DATA 27
/*----------------------------------------------------------------------*/
/* Function Prototypes */
/*----------------------------------------------------------------------*/
#if defined(__OS2__)
#define NP_LOADDS _System
#else
#define NP_LOADDS
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* NPP_* functions are provided by the plugin and called by the navigator. */
#if defined(XP_UNIX)
const char* NPP_GetMIMEDescription(void);
#endif
NPError NP_LOADDS NPP_New(NPMIMEType pluginType, NPP instance,
uint16_t mode, int16_t argc, char* argn[],
char* argv[], NPSavedData* saved);
NPError NP_LOADDS NPP_Destroy(NPP instance, NPSavedData** save);
NPError NP_LOADDS NPP_SetWindow(NPP instance, NPWindow* window);
NPError NP_LOADDS NPP_NewStream(NPP instance, NPMIMEType type,
NPStream* stream, NPBool seekable,
uint16_t* stype);
NPError NP_LOADDS NPP_DestroyStream(NPP instance, NPStream* stream,
NPReason reason);
int32_t NP_LOADDS NPP_WriteReady(NPP instance, NPStream* stream);
int32_t NP_LOADDS NPP_Write(NPP instance, NPStream* stream, int32_t offset,
int32_t len, void* buffer);
void NP_LOADDS NPP_StreamAsFile(NPP instance, NPStream* stream,
const char* fname);
void NP_LOADDS NPP_Print(NPP instance, NPPrint* platformPrint);
int16_t NP_LOADDS NPP_HandleEvent(NPP instance, void* event);
void NP_LOADDS NPP_URLNotify(NPP instance, const char* url,
NPReason reason, void* notifyData);
NPError NP_LOADDS NPP_GetValue(NPP instance, NPPVariable variable, void *value);
NPError NP_LOADDS NPP_SetValue(NPP instance, NPNVariable variable, void *value);
NPBool NP_LOADDS NPP_GotFocus(NPP instance, NPFocusDirection direction);
void NP_LOADDS NPP_LostFocus(NPP instance);
void NP_LOADDS NPP_URLRedirectNotify(NPP instance, const char* url, int32_t status, void* notifyData);
NPError NP_LOADDS NPP_ClearSiteData(const char* site, uint64_t flags, uint64_t maxAge);
char** NP_LOADDS NPP_GetSitesWithData(void);
/* NPN_* functions are provided by the navigator and called by the plugin. */
void NP_LOADDS NPN_Version(int* plugin_major, int* plugin_minor,
int* netscape_major, int* netscape_minor);
NPError NP_LOADDS NPN_GetURLNotify(NPP instance, const char* url,
const char* target, void* notifyData);
NPError NP_LOADDS NPN_GetURL(NPP instance, const char* url,
const char* target);
NPError NP_LOADDS NPN_PostURLNotify(NPP instance, const char* url,
const char* target, uint32_t len,
const char* buf, NPBool file,
void* notifyData);
NPError NP_LOADDS NPN_PostURL(NPP instance, const char* url,
const char* target, uint32_t len,
const char* buf, NPBool file);
NPError NP_LOADDS NPN_RequestRead(NPStream* stream, NPByteRange* rangeList);
NPError NP_LOADDS NPN_NewStream(NPP instance, NPMIMEType type,
const char* target, NPStream** stream);
int32_t NP_LOADDS NPN_Write(NPP instance, NPStream* stream, int32_t len,
void* buffer);
NPError NP_LOADDS NPN_DestroyStream(NPP instance, NPStream* stream,
NPReason reason);
void NP_LOADDS NPN_Status(NPP instance, const char* message);
const char* NP_LOADDS NPN_UserAgent(NPP instance);
void* NP_LOADDS NPN_MemAlloc(uint32_t size);
void NP_LOADDS NPN_MemFree(void* ptr);
uint32_t NP_LOADDS NPN_MemFlush(uint32_t size);
void NP_LOADDS NPN_ReloadPlugins(NPBool reloadPages);
NPError NP_LOADDS NPN_GetValue(NPP instance, NPNVariable variable,
void *value);
NPError NP_LOADDS NPN_SetValue(NPP instance, NPPVariable variable,
void *value);
void NP_LOADDS NPN_InvalidateRect(NPP instance, NPRect *invalidRect);
void NP_LOADDS NPN_InvalidateRegion(NPP instance,
NPRegion invalidRegion);
void NP_LOADDS NPN_ForceRedraw(NPP instance);
void NP_LOADDS NPN_PushPopupsEnabledState(NPP instance, NPBool enabled);
void NP_LOADDS NPN_PopPopupsEnabledState(NPP instance);
void NP_LOADDS NPN_PluginThreadAsyncCall(NPP instance,
void (*func) (void *),
void *userData);
NPError NP_LOADDS NPN_GetValueForURL(NPP instance, NPNURLVariable variable,
const char *url, char **value,
uint32_t *len);
NPError NP_LOADDS NPN_SetValueForURL(NPP instance, NPNURLVariable variable,
const char *url, const char *value,
uint32_t len);
NPError NP_LOADDS NPN_GetAuthenticationInfo(NPP instance,
const char *protocol,
const char *host, int32_t port,
const char *scheme,
const char *realm,
char **username, uint32_t *ulen,
char **password,
uint32_t *plen);
uint32_t NP_LOADDS NPN_ScheduleTimer(NPP instance, uint32_t interval, NPBool repeat, void (*timerFunc)(NPP npp, uint32_t timerID));
void NP_LOADDS NPN_UnscheduleTimer(NPP instance, uint32_t timerID);
NPError NP_LOADDS NPN_PopUpContextMenu(NPP instance, NPMenu* menu);
NPBool NP_LOADDS NPN_ConvertPoint(NPP instance, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace);
NPBool NP_LOADDS NPN_HandleEvent(NPP instance, void *event, NPBool handled);
NPBool NP_LOADDS NPN_UnfocusInstance(NPP instance, NPFocusDirection direction);
void NP_LOADDS NPN_URLRedirectResponse(NPP instance, void* notifyData, NPBool allow);
#ifdef __cplusplus
} /* end extern "C" */
#endif
#endif /* RC_INVOKED */
#if defined(__OS2__)
#pragma pack()
#endif
#endif /* npapi_h_ */

View File

@ -0,0 +1,322 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef npfunctions_h_
#define npfunctions_h_
#ifdef __OS2__
#pragma pack(1)
#define NP_LOADDS _System
#else
#define NP_LOADDS
#endif
#include "npapi.h"
#include "npruntime.h"
typedef NPError (* NP_LOADDS NPP_NewProcPtr)(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData* saved);
typedef NPError (* NP_LOADDS NPP_DestroyProcPtr)(NPP instance, NPSavedData** save);
typedef NPError (* NP_LOADDS NPP_SetWindowProcPtr)(NPP instance, NPWindow* window);
typedef NPError (* NP_LOADDS NPP_NewStreamProcPtr)(NPP instance, NPMIMEType type, NPStream* stream, NPBool seekable, uint16_t* stype);
typedef NPError (* NP_LOADDS NPP_DestroyStreamProcPtr)(NPP instance, NPStream* stream, NPReason reason);
typedef int32_t (* NP_LOADDS NPP_WriteReadyProcPtr)(NPP instance, NPStream* stream);
typedef int32_t (* NP_LOADDS NPP_WriteProcPtr)(NPP instance, NPStream* stream, int32_t offset, int32_t len, void* buffer);
typedef void (* NP_LOADDS NPP_StreamAsFileProcPtr)(NPP instance, NPStream* stream, const char* fname);
typedef void (* NP_LOADDS NPP_PrintProcPtr)(NPP instance, NPPrint* platformPrint);
typedef int16_t (* NP_LOADDS NPP_HandleEventProcPtr)(NPP instance, void* event);
typedef void (* NP_LOADDS NPP_URLNotifyProcPtr)(NPP instance, const char* url, NPReason reason, void* notifyData);
/* Any NPObjects returned to the browser via NPP_GetValue should be retained
by the plugin on the way out. The browser is responsible for releasing. */
typedef NPError (* NP_LOADDS NPP_GetValueProcPtr)(NPP instance, NPPVariable variable, void *ret_value);
typedef NPError (* NP_LOADDS NPP_SetValueProcPtr)(NPP instance, NPNVariable variable, void *value);
typedef NPBool (* NP_LOADDS NPP_GotFocusPtr)(NPP instance, NPFocusDirection direction);
typedef void (* NP_LOADDS NPP_LostFocusPtr)(NPP instance);
typedef void (* NP_LOADDS NPP_URLRedirectNotifyPtr)(NPP instance, const char* url, int32_t status, void* notifyData);
typedef NPError (* NP_LOADDS NPP_ClearSiteDataPtr)(const char* site, uint64_t flags, uint64_t maxAge);
typedef char** (* NP_LOADDS NPP_GetSitesWithDataPtr)(void);
typedef NPError (*NPN_GetValueProcPtr)(NPP instance, NPNVariable variable, void *ret_value);
typedef NPError (*NPN_SetValueProcPtr)(NPP instance, NPPVariable variable, void *value);
typedef NPError (*NPN_GetURLNotifyProcPtr)(NPP instance, const char* url, const char* window, void* notifyData);
typedef NPError (*NPN_PostURLNotifyProcPtr)(NPP instance, const char* url, const char* window, uint32_t len, const char* buf, NPBool file, void* notifyData);
typedef NPError (*NPN_GetURLProcPtr)(NPP instance, const char* url, const char* window);
typedef NPError (*NPN_PostURLProcPtr)(NPP instance, const char* url, const char* window, uint32_t len, const char* buf, NPBool file);
typedef NPError (*NPN_RequestReadProcPtr)(NPStream* stream, NPByteRange* rangeList);
typedef NPError (*NPN_NewStreamProcPtr)(NPP instance, NPMIMEType type, const char* window, NPStream** stream);
typedef int32_t (*NPN_WriteProcPtr)(NPP instance, NPStream* stream, int32_t len, void* buffer);
typedef NPError (*NPN_DestroyStreamProcPtr)(NPP instance, NPStream* stream, NPReason reason);
typedef void (*NPN_StatusProcPtr)(NPP instance, const char* message);
/* Browser manages the lifetime of the buffer returned by NPN_UserAgent, don't
depend on it sticking around and don't free it. */
typedef const char* (*NPN_UserAgentProcPtr)(NPP instance);
typedef void* (*NPN_MemAllocProcPtr)(uint32_t size);
typedef void (*NPN_MemFreeProcPtr)(void* ptr);
typedef uint32_t (*NPN_MemFlushProcPtr)(uint32_t size);
typedef void (*NPN_ReloadPluginsProcPtr)(NPBool reloadPages);
typedef void* (*NPN_GetJavaEnvProcPtr)(void);
typedef void* (*NPN_GetJavaPeerProcPtr)(NPP instance);
typedef void (*NPN_InvalidateRectProcPtr)(NPP instance, NPRect *rect);
typedef void (*NPN_InvalidateRegionProcPtr)(NPP instance, NPRegion region);
typedef void (*NPN_ForceRedrawProcPtr)(NPP instance);
typedef NPIdentifier (*NPN_GetStringIdentifierProcPtr)(const NPUTF8* name);
typedef void (*NPN_GetStringIdentifiersProcPtr)(const NPUTF8** names, int32_t nameCount, NPIdentifier* identifiers);
typedef NPIdentifier (*NPN_GetIntIdentifierProcPtr)(int32_t intid);
typedef bool (*NPN_IdentifierIsStringProcPtr)(NPIdentifier identifier);
typedef NPUTF8* (*NPN_UTF8FromIdentifierProcPtr)(NPIdentifier identifier);
typedef int32_t (*NPN_IntFromIdentifierProcPtr)(NPIdentifier identifier);
typedef NPObject* (*NPN_CreateObjectProcPtr)(NPP npp, NPClass *aClass);
typedef NPObject* (*NPN_RetainObjectProcPtr)(NPObject *obj);
typedef void (*NPN_ReleaseObjectProcPtr)(NPObject *obj);
typedef bool (*NPN_InvokeProcPtr)(NPP npp, NPObject* obj, NPIdentifier methodName, const NPVariant *args, uint32_t argCount, NPVariant *result);
typedef bool (*NPN_InvokeDefaultProcPtr)(NPP npp, NPObject* obj, const NPVariant *args, uint32_t argCount, NPVariant *result);
typedef bool (*NPN_EvaluateProcPtr)(NPP npp, NPObject *obj, NPString *script, NPVariant *result);
typedef bool (*NPN_GetPropertyProcPtr)(NPP npp, NPObject *obj, NPIdentifier propertyName, NPVariant *result);
typedef bool (*NPN_SetPropertyProcPtr)(NPP npp, NPObject *obj, NPIdentifier propertyName, const NPVariant *value);
typedef bool (*NPN_RemovePropertyProcPtr)(NPP npp, NPObject *obj, NPIdentifier propertyName);
typedef bool (*NPN_HasPropertyProcPtr)(NPP npp, NPObject *obj, NPIdentifier propertyName);
typedef bool (*NPN_HasMethodProcPtr)(NPP npp, NPObject *obj, NPIdentifier propertyName);
typedef void (*NPN_ReleaseVariantValueProcPtr)(NPVariant *variant);
typedef void (*NPN_SetExceptionProcPtr)(NPObject *obj, const NPUTF8 *message);
typedef void (*NPN_PushPopupsEnabledStateProcPtr)(NPP npp, NPBool enabled);
typedef void (*NPN_PopPopupsEnabledStateProcPtr)(NPP npp);
typedef bool (*NPN_EnumerateProcPtr)(NPP npp, NPObject *obj, NPIdentifier **identifier, uint32_t *count);
typedef void (*NPN_PluginThreadAsyncCallProcPtr)(NPP instance, void (*func)(void *), void *userData);
typedef bool (*NPN_ConstructProcPtr)(NPP npp, NPObject* obj, const NPVariant *args, uint32_t argCount, NPVariant *result);
typedef NPError (*NPN_GetValueForURLPtr)(NPP npp, NPNURLVariable variable, const char *url, char **value, uint32_t *len);
typedef NPError (*NPN_SetValueForURLPtr)(NPP npp, NPNURLVariable variable, const char *url, const char *value, uint32_t len);
typedef NPError (*NPN_GetAuthenticationInfoPtr)(NPP npp, const char *protocol, const char *host, int32_t port, const char *scheme, const char *realm, char **username, uint32_t *ulen, char **password, uint32_t *plen);
typedef uint32_t (*NPN_ScheduleTimerPtr)(NPP instance, uint32_t interval, NPBool repeat, void (*timerFunc)(NPP npp, uint32_t timerID));
typedef void (*NPN_UnscheduleTimerPtr)(NPP instance, uint32_t timerID);
typedef NPError (*NPN_PopUpContextMenuPtr)(NPP instance, NPMenu* menu);
typedef NPBool (*NPN_ConvertPointPtr)(NPP instance, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace);
typedef NPBool (*NPN_HandleEventPtr)(NPP instance, void *event, NPBool handled);
typedef NPBool (*NPN_UnfocusInstancePtr)(NPP instance, NPFocusDirection direction);
typedef void (*NPN_URLRedirectResponsePtr)(NPP instance, void* notifyData, NPBool allow);
typedef struct _NPPluginFuncs {
uint16_t size;
uint16_t version;
NPP_NewProcPtr newp;
NPP_DestroyProcPtr destroy;
NPP_SetWindowProcPtr setwindow;
NPP_NewStreamProcPtr newstream;
NPP_DestroyStreamProcPtr destroystream;
NPP_StreamAsFileProcPtr asfile;
NPP_WriteReadyProcPtr writeready;
NPP_WriteProcPtr write;
NPP_PrintProcPtr print;
NPP_HandleEventProcPtr event;
NPP_URLNotifyProcPtr urlnotify;
void* javaClass;
NPP_GetValueProcPtr getvalue;
NPP_SetValueProcPtr setvalue;
NPP_GotFocusPtr gotfocus;
NPP_LostFocusPtr lostfocus;
NPP_URLRedirectNotifyPtr urlredirectnotify;
NPP_ClearSiteDataPtr clearsitedata;
NPP_GetSitesWithDataPtr getsiteswithdata;
} NPPluginFuncs;
typedef struct _NPNetscapeFuncs {
uint16_t size;
uint16_t version;
NPN_GetURLProcPtr geturl;
NPN_PostURLProcPtr posturl;
NPN_RequestReadProcPtr requestread;
NPN_NewStreamProcPtr newstream;
NPN_WriteProcPtr write;
NPN_DestroyStreamProcPtr destroystream;
NPN_StatusProcPtr status;
NPN_UserAgentProcPtr uagent;
NPN_MemAllocProcPtr memalloc;
NPN_MemFreeProcPtr memfree;
NPN_MemFlushProcPtr memflush;
NPN_ReloadPluginsProcPtr reloadplugins;
NPN_GetJavaEnvProcPtr getJavaEnv;
NPN_GetJavaPeerProcPtr getJavaPeer;
NPN_GetURLNotifyProcPtr geturlnotify;
NPN_PostURLNotifyProcPtr posturlnotify;
NPN_GetValueProcPtr getvalue;
NPN_SetValueProcPtr setvalue;
NPN_InvalidateRectProcPtr invalidaterect;
NPN_InvalidateRegionProcPtr invalidateregion;
NPN_ForceRedrawProcPtr forceredraw;
NPN_GetStringIdentifierProcPtr getstringidentifier;
NPN_GetStringIdentifiersProcPtr getstringidentifiers;
NPN_GetIntIdentifierProcPtr getintidentifier;
NPN_IdentifierIsStringProcPtr identifierisstring;
NPN_UTF8FromIdentifierProcPtr utf8fromidentifier;
NPN_IntFromIdentifierProcPtr intfromidentifier;
NPN_CreateObjectProcPtr createobject;
NPN_RetainObjectProcPtr retainobject;
NPN_ReleaseObjectProcPtr releaseobject;
NPN_InvokeProcPtr invoke;
NPN_InvokeDefaultProcPtr invokeDefault;
NPN_EvaluateProcPtr evaluate;
NPN_GetPropertyProcPtr getproperty;
NPN_SetPropertyProcPtr setproperty;
NPN_RemovePropertyProcPtr removeproperty;
NPN_HasPropertyProcPtr hasproperty;
NPN_HasMethodProcPtr hasmethod;
NPN_ReleaseVariantValueProcPtr releasevariantvalue;
NPN_SetExceptionProcPtr setexception;
NPN_PushPopupsEnabledStateProcPtr pushpopupsenabledstate;
NPN_PopPopupsEnabledStateProcPtr poppopupsenabledstate;
NPN_EnumerateProcPtr enumerate;
NPN_PluginThreadAsyncCallProcPtr pluginthreadasynccall;
NPN_ConstructProcPtr construct;
NPN_GetValueForURLPtr getvalueforurl;
NPN_SetValueForURLPtr setvalueforurl;
NPN_GetAuthenticationInfoPtr getauthenticationinfo;
NPN_ScheduleTimerPtr scheduletimer;
NPN_UnscheduleTimerPtr unscheduletimer;
NPN_PopUpContextMenuPtr popupcontextmenu;
NPN_ConvertPointPtr convertpoint;
NPN_HandleEventPtr handleevent;
NPN_UnfocusInstancePtr unfocusinstance;
NPN_URLRedirectResponsePtr urlredirectresponse;
} NPNetscapeFuncs;
#ifdef XP_MACOSX
/*
* Mac OS X version(s) of NP_GetMIMEDescription(const char *)
* These can be called to retreive MIME information from the plugin dynamically
*
* Note: For compatibility with Quicktime, BPSupportedMIMEtypes is another way
* to get mime info from the plugin only on OSX and may not be supported
* in furture version -- use NP_GetMIMEDescription instead
*/
enum
{
kBPSupportedMIMETypesStructVers_1 = 1
};
typedef struct _BPSupportedMIMETypes
{
SInt32 structVersion; /* struct version */
Handle typeStrings; /* STR# formated handle, allocated by plug-in */
Handle infoStrings; /* STR# formated handle, allocated by plug-in */
} BPSupportedMIMETypes;
OSErr BP_GetSupportedMIMETypes(BPSupportedMIMETypes *mimeInfo, UInt32 flags);
#define NP_GETMIMEDESCRIPTION_NAME "NP_GetMIMEDescription"
typedef const char* (*NP_GetMIMEDescriptionProcPtr)(void);
typedef OSErr (*BP_GetSupportedMIMETypesProcPtr)(BPSupportedMIMETypes*, UInt32);
#endif
#if defined(_WIN32)
#define OSCALL WINAPI
#else
#if defined(__OS2__)
#define OSCALL _System
#else
#define OSCALL
#endif
#endif
#if defined(XP_UNIX)
/* GCC 3.3 and later support the visibility attribute. */
#if defined(__GNUC__) && ((__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 3))
#define NP_VISIBILITY_DEFAULT __attribute__((visibility("default")))
#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
#define NP_VISIBILITY_DEFAULT __global
#else
#define NP_VISIBILITY_DEFAULT
#endif
#define NP_EXPORT(__type) NP_VISIBILITY_DEFAULT __type
#endif
#if defined(_WIN32) || defined (__OS2__)
#ifdef __cplusplus
extern "C" {
#endif
/* plugin meta member functions */
#if defined(__OS2__)
typedef struct _NPPluginData { /* Alternate OS2 Plugin interface */
char *pMimeTypes;
char *pFileExtents;
char *pFileOpenTemplate;
char *pProductName;
char *pProductDescription;
unsigned long dwProductVersionMS;
unsigned long dwProductVersionLS;
} NPPluginData;
typedef NPError (*NP_GetPluginDataFunc)(NPPluginData*);
NPError OSCALL NP_GetPluginData(NPPluginData * pPluginData);
#endif
typedef NPError (*NP_GetEntryPointsFunc)(NPPluginFuncs*);
NPError OSCALL NP_GetEntryPoints(NPPluginFuncs* pFuncs);
typedef NPError (*NP_InitializeFunc)(NPNetscapeFuncs*);
NPError OSCALL NP_Initialize(NPNetscapeFuncs* bFuncs);
typedef NPError (*NP_ShutdownFunc)(void);
NPError OSCALL NP_Shutdown(void);
typedef const char* (*NP_GetMIMEDescriptionFunc)(void);
const char* NP_GetMIMEDescription(void);
#ifdef __cplusplus
}
#endif
#endif
#if defined(__OS2__)
#pragma pack()
#endif
#ifdef XP_UNIX
#ifdef __cplusplus
extern "C" {
#endif
typedef char* (*NP_GetPluginVersionFunc)(void);
NP_EXPORT(char*) NP_GetPluginVersion(void);
typedef const char* (*NP_GetMIMEDescriptionFunc)(void);
NP_EXPORT(const char*) NP_GetMIMEDescription(void);
#ifdef XP_MACOSX
typedef NPError (*NP_InitializeFunc)(NPNetscapeFuncs*);
NP_EXPORT(NPError) NP_Initialize(NPNetscapeFuncs* bFuncs);
typedef NPError (*NP_GetEntryPointsFunc)(NPPluginFuncs*);
NP_EXPORT(NPError) NP_GetEntryPoints(NPPluginFuncs* pFuncs);
#else
typedef NPError (*NP_InitializeFunc)(NPNetscapeFuncs*, NPPluginFuncs*);
NP_EXPORT(NPError) NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs);
#endif
typedef NPError (*NP_ShutdownFunc)(void);
NP_EXPORT(NPError) NP_Shutdown(void);
typedef NPError (*NP_GetValueFunc)(void *, NPPVariable, void *);
NP_EXPORT(NPError) NP_GetValue(void *future, NPPVariable aVariable, void *aValue);
#ifdef __cplusplus
}
#endif
#endif
#endif /* npfunctions_h_ */

View File

@ -0,0 +1,393 @@
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* Copyright (c) 2004, Apple Computer, Inc. and The Mozilla Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the names of Apple Computer, Inc. ("Apple") or The Mozilla
* Foundation ("Mozilla") nor the names of their contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE, MOZILLA AND THEIR CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE, MOZILLA OR
* THEIR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifndef _NP_RUNTIME_H_
#define _NP_RUNTIME_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "nptypes.h"
/*
This API is used to facilitate binding code written in C to script
objects. The API in this header does not assume the presence of a
user agent. That is, it can be used to bind C code to scripting
environments outside of the context of a user agent.
However, the normal use of the this API is in the context of a
scripting environment running in a browser or other user agent.
In particular it is used to support the extended Netscape
script-ability API for plugins (NP-SAP). NP-SAP is an extension
of the Netscape plugin API. As such we have adopted the use of
the "NP" prefix for this API.
The following NP{N|P}Variables were added to the Netscape plugin
API (in npapi.h):
NPNVWindowNPObject
NPNVPluginElementNPObject
NPPVpluginScriptableNPObject
These variables are exposed through NPN_GetValue() and
NPP_GetValue() (respectively) and are used to establish the
initial binding between the user agent and native code. The DOM
objects in the user agent can be examined and manipulated using
the NPN_ functions that operate on NPObjects described in this
header.
To the extent possible the assumptions about the scripting
language used by the scripting environment have been minimized.
*/
#define NP_BEGIN_MACRO do {
#define NP_END_MACRO } while (0)
/*
Objects (non-primitive data) passed between 'C' and script is
always wrapped in an NPObject. The 'interface' of an NPObject is
described by an NPClass.
*/
typedef struct NPObject NPObject;
typedef struct NPClass NPClass;
typedef char NPUTF8;
typedef struct _NPString {
const NPUTF8 *UTF8Characters;
uint32_t UTF8Length;
} NPString;
typedef enum {
NPVariantType_Void,
NPVariantType_Null,
NPVariantType_Bool,
NPVariantType_Int32,
NPVariantType_Double,
NPVariantType_String,
NPVariantType_Object
} NPVariantType;
typedef struct _NPVariant {
NPVariantType type;
union {
bool boolValue;
int32_t intValue;
double doubleValue;
NPString stringValue;
NPObject *objectValue;
} value;
} NPVariant;
/*
NPN_ReleaseVariantValue is called on all 'out' parameters
references. Specifically it is to be called on variants that own
their value, as is the case with all non-const NPVariant*
arguments after a successful call to any methods (except this one)
in this API.
After calling NPN_ReleaseVariantValue, the type of the variant
will be NPVariantType_Void.
*/
void NPN_ReleaseVariantValue(NPVariant *variant);
#define NPVARIANT_IS_VOID(_v) ((_v).type == NPVariantType_Void)
#define NPVARIANT_IS_NULL(_v) ((_v).type == NPVariantType_Null)
#define NPVARIANT_IS_BOOLEAN(_v) ((_v).type == NPVariantType_Bool)
#define NPVARIANT_IS_INT32(_v) ((_v).type == NPVariantType_Int32)
#define NPVARIANT_IS_DOUBLE(_v) ((_v).type == NPVariantType_Double)
#define NPVARIANT_IS_STRING(_v) ((_v).type == NPVariantType_String)
#define NPVARIANT_IS_OBJECT(_v) ((_v).type == NPVariantType_Object)
#define NPVARIANT_TO_BOOLEAN(_v) ((_v).value.boolValue)
#define NPVARIANT_TO_INT32(_v) ((_v).value.intValue)
#define NPVARIANT_TO_DOUBLE(_v) ((_v).value.doubleValue)
#define NPVARIANT_TO_STRING(_v) ((_v).value.stringValue)
#define NPVARIANT_TO_OBJECT(_v) ((_v).value.objectValue)
#define VOID_TO_NPVARIANT(_v) \
NP_BEGIN_MACRO \
(_v).type = NPVariantType_Void; \
(_v).value.objectValue = NULL; \
NP_END_MACRO
#define NULL_TO_NPVARIANT(_v) \
NP_BEGIN_MACRO \
(_v).type = NPVariantType_Null; \
(_v).value.objectValue = NULL; \
NP_END_MACRO
#define BOOLEAN_TO_NPVARIANT(_val, _v) \
NP_BEGIN_MACRO \
(_v).type = NPVariantType_Bool; \
(_v).value.boolValue = !!(_val); \
NP_END_MACRO
#define INT32_TO_NPVARIANT(_val, _v) \
NP_BEGIN_MACRO \
(_v).type = NPVariantType_Int32; \
(_v).value.intValue = _val; \
NP_END_MACRO
#define DOUBLE_TO_NPVARIANT(_val, _v) \
NP_BEGIN_MACRO \
(_v).type = NPVariantType_Double; \
(_v).value.doubleValue = _val; \
NP_END_MACRO
#define STRINGZ_TO_NPVARIANT(_val, _v) \
NP_BEGIN_MACRO \
(_v).type = NPVariantType_String; \
NPString str = { _val, (uint32_t)(strlen(_val)) }; \
(_v).value.stringValue = str; \
NP_END_MACRO
#define STRINGN_TO_NPVARIANT(_val, _len, _v) \
NP_BEGIN_MACRO \
(_v).type = NPVariantType_String; \
NPString str = { _val, (uint32_t)(_len) }; \
(_v).value.stringValue = str; \
NP_END_MACRO
#define OBJECT_TO_NPVARIANT(_val, _v) \
NP_BEGIN_MACRO \
(_v).type = NPVariantType_Object; \
(_v).value.objectValue = _val; \
NP_END_MACRO
/*
Type mappings (JavaScript types have been used for illustration
purposes):
JavaScript to C (NPVariant with type:)
undefined NPVariantType_Void
null NPVariantType_Null
Boolean NPVariantType_Bool
Number NPVariantType_Double or NPVariantType_Int32
String NPVariantType_String
Object NPVariantType_Object
C (NPVariant with type:) to JavaScript
NPVariantType_Void undefined
NPVariantType_Null null
NPVariantType_Bool Boolean
NPVariantType_Int32 Number
NPVariantType_Double Number
NPVariantType_String String
NPVariantType_Object Object
*/
typedef void *NPIdentifier;
/*
NPObjects have methods and properties. Methods and properties are
identified with NPIdentifiers. These identifiers may be reflected
in script. NPIdentifiers can be either strings or integers, IOW,
methods and properties can be identified by either strings or
integers (i.e. foo["bar"] vs foo[1]). NPIdentifiers can be
compared using ==. In case of any errors, the requested
NPIdentifier(s) will be NULL. NPIdentifier lifetime is controlled
by the browser. Plugins do not need to worry about memory management
with regards to NPIdentifiers.
*/
NPIdentifier NPN_GetStringIdentifier(const NPUTF8 *name);
void NPN_GetStringIdentifiers(const NPUTF8 **names, int32_t nameCount,
NPIdentifier *identifiers);
NPIdentifier NPN_GetIntIdentifier(int32_t intid);
bool NPN_IdentifierIsString(NPIdentifier identifier);
/*
The NPUTF8 returned from NPN_UTF8FromIdentifier SHOULD be freed.
*/
NPUTF8 *NPN_UTF8FromIdentifier(NPIdentifier identifier);
/*
Get the integer represented by identifier. If identifier is not an
integer identifier, the behaviour is undefined.
*/
int32_t NPN_IntFromIdentifier(NPIdentifier identifier);
/*
NPObject behavior is implemented using the following set of
callback functions.
The NPVariant *result argument of these functions (where
applicable) should be released using NPN_ReleaseVariantValue().
*/
typedef NPObject *(*NPAllocateFunctionPtr)(NPP npp, NPClass *aClass);
typedef void (*NPDeallocateFunctionPtr)(NPObject *npobj);
typedef void (*NPInvalidateFunctionPtr)(NPObject *npobj);
typedef bool (*NPHasMethodFunctionPtr)(NPObject *npobj, NPIdentifier name);
typedef bool (*NPInvokeFunctionPtr)(NPObject *npobj, NPIdentifier name,
const NPVariant *args, uint32_t argCount,
NPVariant *result);
typedef bool (*NPInvokeDefaultFunctionPtr)(NPObject *npobj,
const NPVariant *args,
uint32_t argCount,
NPVariant *result);
typedef bool (*NPHasPropertyFunctionPtr)(NPObject *npobj, NPIdentifier name);
typedef bool (*NPGetPropertyFunctionPtr)(NPObject *npobj, NPIdentifier name,
NPVariant *result);
typedef bool (*NPSetPropertyFunctionPtr)(NPObject *npobj, NPIdentifier name,
const NPVariant *value);
typedef bool (*NPRemovePropertyFunctionPtr)(NPObject *npobj,
NPIdentifier name);
typedef bool (*NPEnumerationFunctionPtr)(NPObject *npobj, NPIdentifier **value,
uint32_t *count);
typedef bool (*NPConstructFunctionPtr)(NPObject *npobj,
const NPVariant *args,
uint32_t argCount,
NPVariant *result);
/*
NPObjects returned by create, retain, invoke, and getProperty pass
a reference count to the caller. That is, the callee adds a
reference count which passes to the caller. It is the caller's
responsibility to release the returned object.
NPInvokeFunctionPtr function may return 0 to indicate a void
result.
NPInvalidateFunctionPtr is called by the scripting environment
when the native code is shutdown. Any attempt to message a
NPObject instance after the invalidate callback has been
called will result in undefined behavior, even if the native code
is still retaining those NPObject instances. (The runtime
will typically return immediately, with 0 or NULL, from an attempt
to dispatch to a NPObject, but this behavior should not be
depended upon.)
The NPEnumerationFunctionPtr function may pass an array of
NPIdentifiers back to the caller. The callee allocs the memory of
the array using NPN_MemAlloc(), and it's the caller's responsibility
to release it using NPN_MemFree().
*/
struct NPClass
{
uint32_t structVersion;
NPAllocateFunctionPtr allocate;
NPDeallocateFunctionPtr deallocate;
NPInvalidateFunctionPtr invalidate;
NPHasMethodFunctionPtr hasMethod;
NPInvokeFunctionPtr invoke;
NPInvokeDefaultFunctionPtr invokeDefault;
NPHasPropertyFunctionPtr hasProperty;
NPGetPropertyFunctionPtr getProperty;
NPSetPropertyFunctionPtr setProperty;
NPRemovePropertyFunctionPtr removeProperty;
NPEnumerationFunctionPtr enumerate;
NPConstructFunctionPtr construct;
};
#define NP_CLASS_STRUCT_VERSION 3
#define NP_CLASS_STRUCT_VERSION_ENUM 2
#define NP_CLASS_STRUCT_VERSION_CTOR 3
#define NP_CLASS_STRUCT_VERSION_HAS_ENUM(npclass) \
((npclass)->structVersion >= NP_CLASS_STRUCT_VERSION_ENUM)
#define NP_CLASS_STRUCT_VERSION_HAS_CTOR(npclass) \
((npclass)->structVersion >= NP_CLASS_STRUCT_VERSION_CTOR)
struct NPObject {
NPClass *_class;
uint32_t referenceCount;
/*
* Additional space may be allocated here by types of NPObjects
*/
};
/*
If the class has an allocate function, NPN_CreateObject invokes
that function, otherwise a NPObject is allocated and
returned. This method will initialize the referenceCount member of
the NPObject to 1.
*/
NPObject *NPN_CreateObject(NPP npp, NPClass *aClass);
/*
Increment the NPObject's reference count.
*/
NPObject *NPN_RetainObject(NPObject *npobj);
/*
Decremented the NPObject's reference count. If the reference
count goes to zero, the class's destroy function is invoke if
specified, otherwise the object is freed directly.
*/
void NPN_ReleaseObject(NPObject *npobj);
/*
Functions to access script objects represented by NPObject.
Calls to script objects are synchronous. If a function returns a
value, it will be supplied via the result NPVariant
argument. Successful calls will return true, false will be
returned in case of an error.
Calls made from plugin code to script must be made from the thread
on which the plugin was initialized.
*/
bool NPN_Invoke(NPP npp, NPObject *npobj, NPIdentifier methodName,
const NPVariant *args, uint32_t argCount, NPVariant *result);
bool NPN_InvokeDefault(NPP npp, NPObject *npobj, const NPVariant *args,
uint32_t argCount, NPVariant *result);
bool NPN_Evaluate(NPP npp, NPObject *npobj, NPString *script,
NPVariant *result);
bool NPN_GetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName,
NPVariant *result);
bool NPN_SetProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName,
const NPVariant *value);
bool NPN_RemoveProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName);
bool NPN_HasProperty(NPP npp, NPObject *npobj, NPIdentifier propertyName);
bool NPN_HasMethod(NPP npp, NPObject *npobj, NPIdentifier methodName);
bool NPN_Enumerate(NPP npp, NPObject *npobj, NPIdentifier **identifier,
uint32_t *count);
bool NPN_Construct(NPP npp, NPObject *npobj, const NPVariant *args,
uint32_t argCount, NPVariant *result);
/*
NPN_SetException may be called to trigger a script exception upon
return from entry points into NPObjects. Typical usage:
NPN_SetException (npobj, message);
*/
void NPN_SetException(NPObject *npobj, const NPUTF8 *message);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,121 @@
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* mozilla.org.
* Portions created by the Initial Developer are Copyright (C) 2004
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Johnny Stenback <jst@mozilla.org> (Original author)
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nptypes_h_
#define nptypes_h_
/*
* Header file for ensuring that C99 types ([u]int32_t, [u]int64_t and bool) and
* true/false macros are available.
*/
#if defined(WIN32) || defined(OS2)
/*
* Win32 and OS/2 don't know C99, so define [u]int_16/32/64 here. The bool
* is predefined tho, both in C and C++.
*/
typedef short int16_t;
typedef unsigned short uint16_t;
typedef int int32_t;
typedef unsigned int uint32_t;
typedef long long int64_t;
typedef unsigned long long uint64_t;
#elif defined(_AIX) || defined(__sun) || defined(__osf__) || defined(IRIX) || defined(HPUX)
/*
* AIX and SunOS ship a inttypes.h header that defines [u]int32_t,
* but not bool for C.
*/
#include <inttypes.h>
#ifndef __cplusplus
typedef int bool;
#define true 1
#define false 0
#endif
#elif defined(bsdi) || defined(FREEBSD) || defined(OPENBSD)
/*
* BSD/OS, FreeBSD, and OpenBSD ship sys/types.h that define int32_t and
* u_int32_t.
*/
#include <sys/types.h>
/*
* BSD/OS ships no header that defines uint32_t, nor bool (for C)
*/
#if defined(bsdi)
typedef u_int32_t uint32_t;
typedef u_int64_t uint64_t;
#if !defined(__cplusplus)
typedef int bool;
#define true 1
#define false 0
#endif
#else
/*
* FreeBSD and OpenBSD define uint32_t and bool.
*/
#include <inttypes.h>
#include <stdbool.h>
#endif
#elif defined(BEOS)
#include <inttypes.h>
#else
/*
* For those that ship a standard C99 stdint.h header file, include
* it. Can't do the same for stdbool.h tho, since some systems ship
* with a stdbool.h file that doesn't compile!
*/
#include <stdint.h>
#ifndef __cplusplus
#if !defined(__GNUC__) || (__GNUC__ > 2 || __GNUC_MINOR__ > 95)
#include <stdbool.h>
#else
/*
* GCC 2.91 can't deal with a typedef for bool, but a #define
* works.
*/
#define bool int
#define true 1
#define false 0
#endif
#endif
#endif
#endif /* nptypes_h_ */

View File

@ -1,5 +1,5 @@
AC_PREREQ(2.63)
AC_INIT([gnome-shell],[3.1.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_INIT([gnome-shell],[3.3.5],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([src/shell-global.c])
@ -9,8 +9,8 @@ AC_CONFIG_AUX_DIR([config])
AC_SUBST([PACKAGE_NAME], ["$PACKAGE_NAME"])
AC_SUBST([PACKAGE_VERSION], ["$PACKAGE_VERSION"])
AM_INIT_AUTOMAKE([1.10 dist-bzip2 no-dist-gzip foreign])
AM_MAINTAINER_MODE
AM_INIT_AUTOMAKE([1.11 no-dist-gzip dist-xz tar-ustar foreign])
AM_MAINTAINER_MODE([enable])
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
@ -36,10 +36,6 @@ AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE, "$GETTEXT_PACKAGE",
PKG_PROG_PKG_CONFIG([0.22])
# GConf stuff
AC_PATH_PROG(GCONFTOOL, gconftool-2, no)
AM_GCONF_SOURCE_2
GLIB_GSETTINGS
# Get a value to substitute into gnome-shell.in
@ -57,7 +53,7 @@ if $PKG_CONFIG --exists gstreamer-0.10 '>=' $GSTREAMER_MIN_VERSION ; then
AC_MSG_RESULT(yes)
build_recorder=true
recorder_modules="gstreamer-0.10 gstreamer-base-0.10 x11"
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes)
PKG_CHECK_MODULES(TEST_SHELL_RECORDER, $recorder_modules clutter-1.0 xfixes gl)
else
AC_MSG_RESULT(no)
fi
@ -66,26 +62,29 @@ AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
CLUTTER_MIN_VERSION=1.7.5
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=0.7.11
MUTTER_MIN_VERSION=3.0.0
GTK_MIN_VERSION=3.0.0
GIO_MIN_VERSION=2.25.9
GJS_MIN_VERSION=1.29.18
MUTTER_MIN_VERSION=3.3.5
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
TELEPATHY_GLIB_MIN_VERSION=0.15.3
TELEPATHY_GLIB_MIN_VERSION=0.15.6
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
POLKIT_MIN_VERSION=0.100
STARTUP_NOTIFICATION_MIN_VERSION=0.11
# Collect more than 20 libraries for a prize!
PKG_CHECK_MODULES(GNOME_SHELL, gio-2.0 >= $GIO_MIN_VERSION
gio-unix-2.0 dbus-glib-1 libxml-2.0
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
libmutter >= $MUTTER_MIN_VERSION
gjs-internals-1.0 >= $GJS_MIN_VERSION
libgnome-menu $recorder_modules gconf-2.0
libgnome-menu-3.0 $recorder_modules
gdk-x11-3.0 libsoup-2.4
gl
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
libstartup-notification-1.0 >= $STARTUP_NOTIFICATION_MIN_VERSION
@ -93,24 +92,21 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-2.0 >= $GIO_MIN_VERSION
libcanberra
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_MIN_VERSION
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes)
polkit-agent-1 >= $POLKIT_MIN_VERSION xfixes
libnm-glib libnm-util gnome-keyring-1)
PKG_CHECK_MODULES(SHELL_PERF_HELPER, gtk+-3.0 gio-2.0)
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-2.0)
PKG_CHECK_MODULES(BROWSER_PLUGIN, gio-2.0 >= $GIO_MIN_VERSION json-glib-1.0 >= 0.13.2)
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])
JHBUILD_TYPELIBDIR="$INTROSPECTION_TYPELIBDIR"
# NM is the only typelib we use that we don't jhbuild
PKG_CHECK_EXISTS([libnm-glib >= 0.8.999],
[NM_TYPELIBDIR=`$PKG_CONFIG --variable=libdir libnm-glib`/girepository-1.0
if test "$INTROSPECTION_TYPELIBDIR" != "$NM_TYPELIBDIR"; then
JHBUILD_TYPELIBDIR="$JHBUILD_TYPELIBDIR:$NM_TYPELIBDIR"
fi])
AC_SUBST(JHBUILD_TYPELIBDIR)
saved_CFLAGS=$CFLAGS
@ -121,8 +117,8 @@ AC_CHECK_FUNCS(JS_NewGlobalObject XFixesCreatePointerBarrier)
CFLAGS=$saved_CFLAGS
LIBS=$saved_LIBS
PKG_CHECK_MODULES(GNOME_SHELL_JS, glib-2.0 gjs-internals-1.0 >= $GJS_MIN_VERSION)
PKG_CHECK_MODULES(ST, clutter-1.0 gtk+-3.0 libcroco-0.6 >= 0.6.2 gnome-desktop-3.0 >= 2.90.0 x11)
PKG_CHECK_MODULES(GDMUSER, dbus-glib-1 gtk+-3.0)
PKG_CHECK_MODULES(TRAY, gtk+-3.0)
PKG_CHECK_MODULES(GVC, libpulse libpulse-mainloop-glib gobject-2.0)
PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 0.1.7)
@ -132,6 +128,7 @@ PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.1.0],
[BLUETOOTH_DIR=`$PKG_CONFIG --variable=applet_libdir gnome-bluetooth-1.0`
BLUETOOTH_LIBS=`$PKG_CONFIG --variable=applet_libs gnome-bluetooth-1.0`
AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"])
AC_SUBST([BLUETOOTH_DIR],["$BLUETOOTH_DIR"])
AC_DEFINE_UNQUOTED([BLUETOOTH_DIR],["$BLUETOOTH_DIR"],[Path to installed GnomeBluetooth typelib and library])
AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet])
AC_SUBST([HAVE_BLUETOOTH],[1])
@ -156,6 +153,17 @@ AC_CHECK_FUNCS(fdwalk)
AC_CHECK_FUNCS(mallinfo)
AC_CHECK_HEADERS([sys/resource.h])
# _NL_TIME_FIRST_WEEKDAY is an enum and not a define
AC_MSG_CHECKING([for _NL_TIME_FIRST_WEEKDAY])
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <langinfo.h>]],
[[nl_langinfo(_NL_TIME_FIRST_WEEKDAY);]])],
[langinfo_ok=yes], [langinfo_ok=no])
AC_MSG_RESULT($langinfo_ok)
if test "$langinfo_ok" = "yes"; then
AC_DEFINE([HAVE__NL_TIME_FIRST_WEEKDAY], [1],
[Define if _NL_TIME_FIRST_WEEKDAY is available])
fi
# Sets GLIB_GENMARSHAL and GLIB_MKENUMS
AM_PATH_GLIB_2_0()
G_IR_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0`
@ -169,11 +177,13 @@ AC_SUBST(GIRDIR)
TYPELIBDIR="$($PKG_CONFIG --variable=typelibdir gobject-introspection-1.0)"
AC_SUBST(TYPELIBDIR)
GTK_DOC_CHECK([1.15], [--flavour no-tmpl])
# Stay command-line compatible with the gnome-common configure option. Here
# minimum/yes/maximum are the same, however.
AC_ARG_ENABLE(compile_warnings,
AS_HELP_STRING([--enable-compile-warnings=@<:@no/minimum/yes/maximum/error@:>@],[Turn on compiler warnings]),,
enable_compile_warnings=error)
enable_compile_warnings=maximum)
changequote(,)dnl
if test "$enable_compile_warnings" != no ; then
@ -189,7 +199,7 @@ if test "$enable_compile_warnings" != no ; then
if test "$enable_compile_warnings" = error ; then
case " $CFLAGS " in
*[\ \ ]-Werror[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Werror" ;;
*) CFLAGS="$CFLAGS -Werror -Wno-error=deprecated-declarations" ;;
esac
fi
fi
@ -197,15 +207,50 @@ fi
changequote([,])dnl
AC_ARG_ENABLE(jhbuild-wrapper-script,
AS_HELP_STRING([--jhbuild-wrapper-script=yes],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)
AS_HELP_STRING([--enable-jhbuild-wrapper-script],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no)
AM_CONDITIONAL(USE_JHBUILD_WRAPPER_SCRIPT, test "x$enable_jhbuild_wrapper_script" = xyes)
AC_MSG_CHECKING([location of system Certificate Authority list])
AC_ARG_WITH(ca-certificates,
[AC_HELP_STRING([--with-ca-certificates=@<:@path@:>@],
[path to system Certificate Authority list])])
if test "$with_ca_certificates" = "no"; then
AC_MSG_RESULT([disabled])
else
if test -z "$with_ca_certificates"; then
for f in /etc/pki/tls/certs/ca-bundle.crt \
/etc/ssl/certs/ca-certificates.crt; do
if test -f "$f"; then
with_ca_certificates="$f"
fi
done
if test -z "$with_ca_certificates"; then
AC_MSG_ERROR([could not find. Use --with-ca-certificates=path to set, or --without-ca-certificates to disable])
fi
fi
AC_MSG_RESULT($with_ca_certificates)
AC_DEFINE_UNQUOTED(SHELL_SYSTEM_CA_FILE, ["$with_ca_certificates"], [The system TLS CA list])
fi
AC_SUBST(SHELL_SYSTEM_CA_FILE,["$with_ca_certificates"])
BROWSER_PLUGIN_DIR="${BROWSER_PLUGIN_DIR:-"\${libdir}/mozilla/plugins"}"
AC_ARG_VAR([BROWSER_PLUGIN_DIR],[Where to install the plugin to])
AC_CONFIG_FILES([
Makefile
data/Makefile
docs/Makefile
docs/reference/Makefile
docs/reference/shell/Makefile
docs/reference/shell/shell-docs.sgml
docs/reference/st/Makefile
docs/reference/st/st-docs.sgml
js/Makefile
js/misc/config.js
src/Makefile
browser-plugin/Makefile
tests/Makefile
po/Makefile.in
man/Makefile

View File

@ -1,5 +1,5 @@
desktopdir=$(datadir)/applications
desktop_DATA = gnome-shell.desktop
desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
# We substitute in bindir so it works as an autostart
# file when built in a non-system prefix
@ -29,6 +29,7 @@ dist_theme_DATA = \
theme/dash-placeholder.svg \
theme/filter-selected-ltr.svg \
theme/filter-selected-rtl.svg \
theme/gdm.css \
theme/gnome-shell.css \
theme/panel-border.svg \
theme/panel-button-border.svg \
@ -58,29 +59,25 @@ gschemas.compiled: $(gsettings_SCHEMAS:.xml=.valid)
all-local: gschemas.compiled
# GConf schemas: provide defaults for keys from Metacity we are overriding
gconfschemadir = @GCONF_SCHEMA_FILE_DIR@
gconfschema_DATA = gnome-shell.schemas
convertdir = $(datadir)/GConf/gsettings
convert_DATA = gnome-shell-overrides.convert
shadersdir = $(pkgdatadir)/shaders
shaders_DATA = \
shaders/dim-window.glsl
install-data-local:
GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $(srcdir)/$(gconfschema_DATA)
EXTRA_DIST = \
gnome-shell.desktop.in.in \
gnome-shell-extension-prefs.desktop.in.in \
$(menu_DATA) \
$(gconfschema_DATA) \
$(shaders_DATA) \
$(convert_DATA) \
org.gnome.shell.gschema.xml.in
CLEANFILES = \
gnome-shell.desktop.in \
gnome-shell-extension-prefs.in \
$(desktop_DATA) \
$(gsettings_SCHEMAS) \
gschemas.compiled

View File

@ -0,0 +1,12 @@
[Desktop Entry]
Type=Application
_Name=GNOME Shell Extension Preferences
_Comment=Configure GNOME Shell Extensions
Exec=@bindir@/gnome-shell-extension-prefs %u
X-GNOME-Bugzilla-Bugzilla=GNOME
X-GNOME-Bugzilla-Product=gnome-shell
X-GNOME-Bugzilla-Component=extensions
X-GNOME-Bugzilla-Version=@VERSION@
Categories=GNOME;GTK;
OnlyShowIn=GNOME;
NoDisplay=true

View File

@ -0,0 +1,5 @@
[org.gnome.shell.overrides]
attach-modal-dialogs = /desktop/gnome/shell/windows/attach_modal_dialogs
button-layout = /desktop/gnome/shell/windows/button_layout
edge-tiling = /desktop/gnome/shell/windows/edge_tiling
workspaces-only-on-primary = /desktop/gnome/shell/windows/workspaces_only_on_primary

View File

@ -1,100 +0,0 @@
<gconfschemafile>
<schemalist>
<!-- Metacity overrides -->
<schema>
<key>/schemas/desktop/gnome/shell/windows/attach_modal_dialogs</key>
<applyto>/desktop/gnome/shell/windows/attach_modal_dialogs</applyto>
<owner>gnome-shell</owner>
<type>bool</type>
<default>true</default>
<locale name="C">
<short>Attach modal dialog to the parent window</short>
<long>
This key overrides /apps/mutter/general/attach_modal_dialogs when
running GNOME Shell.
</long>
</locale>
</schema>
<schema>
<key>/schemas/desktop/gnome/shell/windows/button_layout</key>
<applyto>/desktop/gnome/shell/windows/button_layout</applyto>
<owner>gnome-shell</owner>
<type>string</type>
<default>:close</default>
<locale name="C">
<short>Arrangement of buttons on the titlebar</short>
<long>
Arrangement of buttons on the titlebar. The
value should be a string, such as
"menu:minimize,maximize,spacer,close"; the colon separates the
left corner of the window from the right corner, and
the button names are comma-separated. Duplicate buttons
are not allowed. Unknown button names are silently ignored
so that buttons can be added in future gnome-shell versions
without breaking older versions.
A special spacer tag can be used to insert some space between
two adjacent buttons.
This key overrides /apps/metacity/general/button_layout when
running GNOME Shell.
</long>
</locale>
</schema>
<schema>
<key>/schemas/desktop/gnome/shell/windows/edge_tiling</key>
<applyto>/desktop/gnome/shell/windows/edge_tiling</applyto>
<owner>gnome-shell</owner>
<type>bool</type>
<default>true</default>
<locale name="C">
<short>enable edge tiling when dropping windows on screen edges</short>
<long>
If enabled, dropping windows on vertical screen edges maximizes them
vertically and resizes them horizontally to cover half of the
available area. Dropping windows on the top screen edge maximizes them
completely.
This key overrides /apps/metacity/general/edge_tiling when
running GNOME Shell.
</long>
</locale>
</schema>
<schema>
<key>/schemas/desktop/gnome/shell/windows/theme</key>
<applyto>/desktop/gnome/shell/windows/theme</applyto>
<owner>gnome-shell</owner>
<type>string</type>
<default>Adwaita</default>
<locale name="C">
<short>Current theme</short>
<long>
The theme determines the appearance of window borders,
titlebar, and so forth.
This key overrides /apps/metacity/general/theme when
running GNOME Shell.
</long>
</locale>
</schema>
<schema>
<key>/schemas/desktop/gnome/shell/windows/workspaces_only_on_primary</key>
<applyto>/desktop/gnome/shell/windows/workspaces_only_on_primary</applyto>
<owner>gnome-shell</owner>
<type>bool</type>
<default>true</default>
<locale name="C">
<short>Workspaces only on primary monitor</short>
<long>
This key overrides /apps/mutter/general/workspaces_only_on_primary when
running GNOME Shell.
</long>
</locale>
</schema>
</schemalist>
</gconfschemafile>

View File

@ -11,22 +11,14 @@
using the Alt-F2 dialog.
</_description>
</key>
<key name="disabled-extensions" type="as">
<default>[]</default>
<_summary>Uuids of extensions to disable</_summary>
<_description>
GNOME Shell extensions have a uuid property; this key lists extensions
which should not be loaded. This setting overrides enabled-extensions
for extensions that appear in both lists.
</_description>
</key>
<key name="enabled-extensions" type="as">
<default>[]</default>
<_summary>Uuids of extensions to enable</_summary>
<_description>
GNOME Shell extensions have a uuid property; this key lists extensions
which should be loaded. disabled-extensions overrides this setting for
extensions that appear in both lists.
which should be loaded. Any extension that wants to be loaded needs
to be in this list. You can also manipulate this list with the
EnableExtension and DisableExtension DBus methods on org.gnome.Shell.
</_description>
</key>
<key name="enable-app-monitoring" type="b">
@ -40,7 +32,7 @@
</_description>
</key>
<key name="favorite-apps" type="as">
<default>[ 'mozilla-firefox.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'openoffice.org-writer.desktop', 'nautilus.desktop' ]</default>
<default>[ 'epiphany.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'libreoffice-writer.desktop', 'nautilus.desktop', 'gnome-documents.desktop' ]</default>
<_summary>List of desktop file IDs for favorite applications</_summary>
<_description>
The applications corresponding to these identifiers
@ -59,9 +51,18 @@
<default>[]</default>
<_summary>History for the looking glass dialog</_summary>
</key>
<key name="saved-im-presence" type="i">
<default>1</default>
<_summary></_summary>
</key>
<key name="saved-session-presence" type="i">
<default>0</default>
<_summary></_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="keyboard" schema="org.gnome.shell.keyboard"/>
</schema>
<schema id="org.gnome.shell.calendar" path="/org/gnome/shell/calendar/"
@ -75,6 +76,17 @@
</key>
</schema>
<schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="keyboard-type" type="s">
<default>'touch'</default>
<_summary>Which keyboard to use</_summary>
<_description>
The type of keyboard to use.
</_description>
</key>
</schema>
<schema id="org.gnome.shell.clock" path="/org/gnome/shell/clock/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="show-seconds" type="b">
@ -96,7 +108,7 @@
<schema id="org.gnome.shell.recorder" path="/org/gnome/shell/recorder/"
gettext-domain="@GETTEXT_PACKAGE@">
<key name="framerate" type="i">
<default>15</default>
<default>30</default>
<_summary>Framerate used for recording screencasts.</_summary>
<_description>
The framerate of the resulting screencast recordered
@ -115,7 +127,7 @@
take care of its own output - this might be used to send the output
to an icecast server via shout2send or similar. When unset or set
to an empty value, the default pipeline will be used. This is currently
'videorate ! vp8enc quality=10 speed=2 threads=%T ! queue ! webmmux'
'vp8enc quality=8 speed=6 threads=%T ! queue ! webmmux'
and records to WEBM using the VP8 codec. %T is used as a placeholder
for a guess at the optimal thread count on the system.
</_description>
@ -130,4 +142,40 @@
</_description>
</key>
</schema>
<schema id="org.gnome.shell.overrides" path="/org/gnome/shell/overrides/">
<key name="attach-modal-dialogs" type="b">
<default>true</default>
<summary>Attach modal dialog to the parent window</summary>
<description>
This key overrides the key in org.gnome.mutter when running
GNOME Shell.
</description>
</key>
<key name="button-layout" type="s">
<default>":close"</default>
<summary>Arrangement of buttons on the titlebar</summary>
<description>
This key overrides the key in org.gnome.desktop.wm.preferences when
running GNOME Shell.
</description>
</key>
<key name="edge-tiling" type="b">
<default>true</default>
<summary>Enable edge tiling when dropping windows on screen edges</summary>
<description>
This key overrides the key in org.gnome.mutter when running GNOME Shell.
</description>
</key>
<key name="workspaces-only-on-primary" type="b">
<default>true</default>
<summary>Workspaces only on primary monitor</summary>
<description>
This key overrides the key in org.gnome.mutter when running GNOME Shell.
</description>
</key>
</schema>
</schemalist>

View File

@ -1,5 +1,5 @@
#version 110
uniform sampler2D sampler0;
uniform sampler2D tex;
uniform float fraction;
uniform float height;
const float c = -0.2;
@ -12,15 +12,16 @@ mat4 contrast = mat4 (1.0 + c, 0.0, 0.0, 0.0,
vec4 off = vec4(0.633, 0.633, 0.633, 0);
void main()
{
vec4 color = texture2D(sampler0, gl_TexCoord[0].st);
float y = height * gl_TexCoord[0][1];
vec4 color = texture2D(tex, cogl_tex_coord_in[0].xy);
float y = height * cogl_tex_coord_in[0].y;
// To reduce contrast, blend with a mid gray
gl_FragColor = color * contrast - off * c;
cogl_color_out = color * contrast - off * c * color.a;
// We only fully dim at a distance of BORDER_MAX_HEIGHT from the edge and
// We only fully dim at a distance of BORDER_MAX_HEIGHT from the top and
// when the fraction is 1.0. For other locations and fractions we linearly
// interpolate back to the original undimmed color.
gl_FragColor = color + (gl_FragColor - color) * min(y / border_max_height, 1.0);
gl_FragColor = color + (gl_FragColor - color) * fraction;
// interpolate back to the original undimmed color, so the top of the window
// is at full color.
cogl_color_out = color + (cogl_color_out - color) * max(min(y / border_max_height, 1.0), 0.0);
cogl_color_out = color + (cogl_color_out - color) * fraction;
}

180
data/theme/gdm.css Normal file
View File

@ -0,0 +1,180 @@
/* Copyright 2011, Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
/* Login Dialog */
.login-dialog-title {
font-size: 14pt;
font-weight: bold;
color: #666666;
padding-bottom: 2em;
}
.login-dialog {
border-radius: 16px;
min-height: 150px;
max-height: 700px;
min-width: 350px;
}
.login-dialog-prompt-fingerprint-message {
font-size: 10.5pt;
}
.login-dialog-user-list-view {
-st-vfade-offset: 1em;
}
.login-dialog-user-list {
spacing: 12px;
}
.login-dialog-user-list-item {
color: #666666;
}
.login-dialog-user-list-item:ltr {
padding-right: 1em;
}
.login-dialog-user-list-item:rtl {
padding-left: 1em;
}
.login-dialog-user-list-item .login-dialog-user-list-item-name {
font-size: 20pt;
padding-left: 1em;
color: #666666;
}
.login-dialog-user-list-item:hover .login-dialog-user-list-item-name {
color: white;
}
.login-dialog-user-list-item:focus .login-dialog-user-list-item-name {
color: white;
text-shadow: black 0px 2px 2px;
}
.login-dialog-user-list-item-vertical-layout {
spacing: 2px;
}
.login-dialog-user-list-item .login-dialog-user-list-item-focus-bin {
background-color: rgba(0,0,0,0.0);
height: 2px;
}
.login-dialog-user-list-item:focus .login-dialog-user-list-item-focus-bin {
background-color: #666666;
}
.login-dialog-user-list-item-icon {
border: 2px solid #8b8b8b;
border-radius: 8px;
width: 64px;
height: 64px;
}
.login-dialog-not-listed-button {
padding-top: 2em;
}
.login-dialog-not-listed-label {
font-size: 14pt;
font-weight: bold;
color: #666666;
}
.login-dialog-not-listed-button:hover .login-dialog-not-listed-label {
color: white;
}
.login-dialog-prompt-layout {
padding-bottom: 32px;
}
.login-dialog-prompt-label {
color: white;
font-size: 20pt;
}
.login-dialog-prompt-entry {
padding: 4px;
border-radius: 4px;
border: 2px solid #5656cc;
color: black;
background-color: white;
caret-color: black;
caret-size: 1px;
width: 15em;
}
.login-dialog-prompt-entry .capslock-warning {
icon-size: 16px;
warning-color: #999;
}
.login-dialog-prompt-entry:insensitive {
color: rgba(0,0,0,0.7);
border: 2px solid #565656;
}
.login-dialog-session-list {
color: #ffffff;
font-size: 10.5pt;
}
.login-dialog-session-list-button {
padding: 4px;
}
.login-dialog-session-list-button:focus {
background-color: #4c4c4c;
}
.login-dialog-session-list-button:active {
background-color: #4c4c4c;
}
.login-dialog-session-list-button:hover {
font-weight: bold;
}
.login-dialog-session-list-scroll-view {
background-gradient-start: rgba(80,80,80,0.3);
background-gradient-end: rgba(80,80,80,0.7);
background-gradient-direction: vertical;
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.9);
border-radius: 8px;
border: 1px solid rgba(80,80,80,1.0);
padding: .5em;
}
.login-dialog-session-list-item:focus {
background-color: #666666;
}
.login-dialog-session-list-triangle {
padding-right: .5em;
}
.login-dialog-session-list-item-box {
spacing: .25em;
}
.login-dialog-session-list-item-dot {
width: .75em;
height: .75em;
}

File diff suppressed because it is too large Load Diff

1
docs/Makefile.am Normal file
View File

@ -0,0 +1 @@
SUBDIRS = reference

View File

@ -0,0 +1 @@
SUBDIRS = shell st

View File

@ -0,0 +1,113 @@
## Process this file with automake to produce Makefile.in
# We require automake 1.6 at least.
AUTOMAKE_OPTIONS = 1.6
# This is a blank Makefile.am for using gtk-doc.
# Copy this to your project's API docs directory and modify the variables to
# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples
# of using the various options.
# The name of the module, e.g. 'glib'.
DOC_MODULE=shell
# Uncomment for versioned docs and specify the version of the module, e.g. '2'.
#DOC_MODULE_VERSION=2
# The top-level SGML file. You can change this if you want to.
DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml
# Directories containing the source code
# gtk-doc will search all .c and .h files beneath these paths
# for inline comments documenting functions and macros.
DOC_SOURCE_DIR=$(top_srcdir)/src
# Extra options to pass to gtkdoc-scangobj. Not normally needed.
SCANGOBJ_OPTIONS=
# Extra options to supply to gtkdoc-scan.
# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED"
SCAN_OPTIONS=--rebuild-types
# Extra options to supply to gtkdoc-mkdb.
# e.g. MKDB_OPTIONS=--xml-mode --output-format=xml
MKDB_OPTIONS=--xml-mode --output-format=xml
# Extra options to supply to gtkdoc-mktmpl
# e.g. MKTMPL_OPTIONS=--only-section-tmpl
MKTMPL_OPTIONS=
# Extra options to supply to gtkdoc-mkhtml
MKHTML_OPTIONS=
# Extra options to supply to gtkdoc-fixref. Not normally needed.
# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html
FIXXREF_OPTIONS=
# Used for dependencies. The docs will be rebuilt if any of these change.
# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h
# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c
HFILE_GLOB=$(top_srcdir)/src/*.h
CFILE_GLOB=$(top_srcdir)/src/*.c
# Extra header to include when scanning, which are not under DOC_SOURCE_DIR
# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h
EXTRA_HFILES=
# Header files or dirs to ignore when scanning. Use base file/dir names
# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h private_code
IGNORE_HFILES= \
calendar-server \
gvc \
hotplug-sniffer \
st \
tray \
gactionmuxer.h \
gactionobservable.h \
gactionobserver.h \
shell-recorder-src.h
# Images to copy into HTML directory.
# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
HTML_IMAGES=
# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
content_files=
# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
# These files must be listed here *and* in content_files
# e.g. expand_content_files=running.sgml
expand_content_files=
# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library.
# Only needed if you are using gtkdoc-scangobj to dynamically query widget
# signals and properties.
# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
GTKDOC_CFLAGS=$(GNOME_SHELL_CFLAGS)
GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(BLUETOOTH_LIBS) $(top_builddir)/src/libgnome-shell.la
# This includes the standard gtk-doc make rules, copied by gtkdocize.
include $(top_srcdir)/gtk-doc.make
# Other files to distribute
# e.g. EXTRA_DIST += version.xml.in
EXTRA_DIST +=
# Files not to distribute
# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types
# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt
DISTCLEANFILES = $(DOC_MODULES).types
# Comment this out if you want 'make check' to test you doc status
# and run some sanity checks
if ENABLE_GTK_DOC
TESTS_ENVIRONMENT = cd $(srcdir) && \
DOC_MODULE=$(DOC_MODULE) DOC_MAIN_SGML_FILE=$(DOC_MAIN_SGML_FILE) \
SRCDIR=$(abs_srcdir) BUILDDIR=$(abs_builddir)
#TESTS = $(GTKDOC_CHECK)
endif
-include $(top_srcdir)/git.mk

View File

@ -0,0 +1,73 @@
<?xml version="1.0"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
[
<!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
]>
<book id="index">
<bookinfo>
<title>Shell Reference Manual</title>
<releaseinfo>
for Shell @VERSION@.
<!--The latest version of this documentation can be found on-line at
<ulink role="online-location" url="http://[SERVER]/shell/index.html">http://[SERVER]/shell/</ulink>.-->
</releaseinfo>
</bookinfo>
<chapter>
<title>Actors</title>
<xi:include href="xml/shell-generic-container.xml"/>
<xi:include href="xml/shell-slicer.xml"/>
<xi:include href="xml/shell-stack.xml"/>
</chapter>
<chapter>
<title>Application tracking</title>
<xi:include href="xml/shell-app.xml"/>
<xi:include href="xml/shell-app-usage.xml"/>
<xi:include href="xml/shell-window-tracker.xml"/>
</chapter>
<chapter>
<title>Search</title>
<xi:include href="xml/shell-app-system.xml"/>
<xi:include href="xml/shell-contact-system.xml"/>
<xi:include href="xml/shell-doc-system.xml"/>
</chapter>
<chapter>
<title>Tray Icons</title>
<xi:include href="xml/shell-embedded-window.xml"/>
<xi:include href="xml/shell-gtk-embed.xml"/>
<xi:include href="xml/shell-tray-icon.xml"/>
<xi:include href="xml/shell-tray-manager.xml"/>
</chapter>
<chapter>
<title>Recorder</title>
<xi:include href="xml/shell-recorder.xml"/>
<xi:include href="xml/shell-recorder-src.xml"/>
</chapter>
<chapter>
<title>Integration helpers and utilities</title>
<xi:include href="xml/shell-global.xml"/>
<xi:include href="xml/shell-wm.xml"/>
<xi:include href="xml/shell-xfixes-cursor.xml"/>
<xi:include href="xml/shell-util.xml"/>
<xi:include href="xml/shell-mount-operation.xml"/>
<xi:include href="xml/shell-mobile-providers.xml"/>
<xi:include href="xml/shell-network-agent.xml"/>
<xi:include href="xml/shell-polkit-authentication-agent.xml"/>
<xi:include href="xml/shell-tp-client.xml"/>
</chapter>
<chapter id="object-tree">
<title>Object Hierarchy</title>
<xi:include href="xml/tree_index.sgml"/>
</chapter>
<index id="api-index-full">
<title>API Index</title>
<xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
</index>
<index id="deprecated-api-index" role="deprecated">
<title>Index of deprecated API</title>
<xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
</index>
<xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
</book>

View File

@ -0,0 +1,104 @@
## Process this file with automake to produce Makefile.in
# We require automake 1.6 at least.
AUTOMAKE_OPTIONS = 1.6
# This is a blank Makefile.am for using gtk-doc.
# Copy this to your project's API docs directory and modify the variables to
# suit your project. See the GTK+ Makefiles in gtk+/docs/reference for examples
# of using the various options.
# The name of the module, e.g. 'glib'.
DOC_MODULE=st
# Uncomment for versioned docs and specify the version of the module, e.g. '2'.
#DOC_MODULE_VERSION=2
# The top-level SGML file. You can change this if you want to.
DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml
# Directories containing the source code
# gtk-doc will search all .c and .h files beneath these paths
# for inline comments documenting functions and macros.
DOC_SOURCE_DIR=$(top_srcdir)/src/st
# Extra options to pass to gtkdoc-scangobj. Not normally needed.
SCANGOBJ_OPTIONS=
# Extra options to supply to gtkdoc-scan.
# e.g. SCAN_OPTIONS=--deprecated-guards="GTK_DISABLE_DEPRECATED"
SCAN_OPTIONS=--rebuild-types --rebuild-sections
# Extra options to supply to gtkdoc-mkdb.
# e.g. MKDB_OPTIONS=--xml-mode --output-format=xml
MKDB_OPTIONS=--xml-mode --output-format=xml
# Extra options to supply to gtkdoc-mktmpl
# e.g. MKTMPL_OPTIONS=--only-section-tmpl
MKTMPL_OPTIONS=
# Extra options to supply to gtkdoc-mkhtml
MKHTML_OPTIONS=
# Extra options to supply to gtkdoc-fixref. Not normally needed.
# e.g. FIXXREF_OPTIONS=--extra-dir=../gdk-pixbuf/html --extra-dir=../gdk/html
FIXXREF_OPTIONS=
# Used for dependencies. The docs will be rebuilt if any of these change.
# e.g. HFILE_GLOB=$(top_srcdir)/gtk/*.h
# e.g. CFILE_GLOB=$(top_srcdir)/gtk/*.c
HFILE_GLOB=$(top_srcdir)/src/st/*.h
CFILE_GLOB=$(top_srcdir)/src/st/*.c
# Extra header to include when scanning, which are not under DOC_SOURCE_DIR
# e.g. EXTRA_HFILES=$(top_srcdir}/contrib/extra.h
EXTRA_HFILES=
# Header files or dirs to ignore when scanning. Use base file/dir names
# e.g. IGNORE_HFILES=gtkdebug.h gtkintl.h private_code
IGNORE_HFILES=st-private.h st-theme-node-private.h
# Images to copy into HTML directory.
# e.g. HTML_IMAGES=$(top_srcdir)/gtk/stock-icons/stock_about_24.png
HTML_IMAGES=
# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
content_files=
# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
# These files must be listed here *and* in content_files
# e.g. expand_content_files=running.sgml
expand_content_files=
# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library.
# Only needed if you are using gtkdoc-scangobj to dynamically query widget
# signals and properties.
# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
GTKDOC_CFLAGS=
GTKDOC_LIBS=$(top_builddir)/src/libst-1.0.la
# This includes the standard gtk-doc make rules, copied by gtkdocize.
include $(top_srcdir)/gtk-doc.make
# Other files to distribute
# e.g. EXTRA_DIST += version.xml.in
EXTRA_DIST +=
# Files not to distribute
# for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types
# for --rebuild-sections in $(SCAN_OPTIONS) e.g. $(DOC_MODULE)-sections.txt
DISTCLEANFILES = $(DOC_MODULE).types $(DOC_MODULE)-sections.txt
# Comment this out if you want 'make check' to test you doc status
# and run some sanity checks
if ENABLE_GTK_DOC
TESTS_ENVIRONMENT = cd $(srcdir) && \
DOC_MODULE=$(DOC_MODULE) DOC_MAIN_SGML_FILE=$(DOC_MAIN_SGML_FILE) \
SRCDIR=$(abs_srcdir) BUILDDIR=$(abs_builddir)
#TESTS = $(GTKDOC_CHECK)
endif
-include $(top_srcdir)/git.mk

View File

@ -0,0 +1,68 @@
<?xml version="1.0"?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
"http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd"
[
<!ENTITY % local.common.attrib "xmlns:xi CDATA #FIXED 'http://www.w3.org/2003/XInclude'">
]>
<book id="index">
<bookinfo>
<title>St Reference Manual</title>
<releaseinfo>
for St @VERSION@.
<!--The latest version of this documentation can be found on-line at
<ulink role="online-location" url="http://[SERVER]/st/index.html">http://[SERVER]/st/</ulink>.-->
</releaseinfo>
</bookinfo>
<part>
<title>API reference</title>
<chapter id="base">
<title>Abstract classes and Interfaces</title>
<xi:include href="xml/st-widget.xml"/>
<xi:include href="xml/st-widget-accessible.xml"/>
<xi:include href="xml/st-container.xml"/>
<xi:include href="xml/st-scrollable.xml"/>
</chapter>
<chapter id="widgets">
<title>Widgets</title>
<xi:include href="xml/st-button.xml"/>
<xi:include href="xml/st-drawing-area.xml"/>
<xi:include href="xml/st-entry.xml"/>
<xi:include href="xml/st-icon.xml"/>
<xi:include href="xml/st-label.xml"/>
<xi:include href="xml/st-tooltip.xml"/>
</chapter>
<chapter id="containers">
<title>Containers</title>
<xi:include href="xml/st-bin.xml"/>
<xi:include href="xml/st-box-layout.xml"/>
<xi:include href="xml/st-group.xml"/>
<xi:include href="xml/st-overflow-box.xml"/>
<xi:include href="xml/st-scroll-view.xml"/>
<xi:include href="xml/st-table.xml"/>
</chapter>
<chapter id="styling">
<title>Styling</title>
<xi:include href="xml/st-theme.xml"/>
<xi:include href="xml/st-theme-context.xml"/>
<xi:include href="xml/st-theme-node.xml"/>
<xi:include href="xml/st-theme-node-transition.xml"/>
<xi:include href="xml/st-texture-cache.xml"/>
</chapter>
</part>
<chapter id="object-tree">
<title>Object Hierarchy</title>
<xi:include href="xml/tree_index.sgml"/>
</chapter>
<index id="api-index-full">
<title>API Index</title>
<xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
</index>
<index id="deprecated-api-index" role="deprecated">
<title>Index of deprecated API</title>
<xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
</index>
<xi:include href="xml/annotation-glossary.xml"><xi:fallback /></xi:include>
</book>

View File

@ -2,12 +2,20 @@
jsdir = $(pkgdatadir)/js
nobase_dist_js_DATA = \
gdm/batch.js \
gdm/consoleKit.js \
gdm/fingerprint.js \
gdm/loginDialog.js \
gdm/powerMenu.js \
extensionPrefs/main.js \
misc/config.js \
misc/docInfo.js \
misc/extensionUtils.js \
misc/fileUtils.js \
misc/format.js \
misc/gnomeSession.js \
misc/history.js \
misc/jsParse.js \
misc/modemManager.js \
misc/params.js \
misc/screenSaver.js \
@ -20,7 +28,7 @@ nobase_dist_js_DATA = \
ui/autorunManager.js \
ui/boxpointer.js \
ui/calendar.js \
ui/chrome.js \
ui/contactDisplay.js \
ui/ctrlAltTab.js \
ui/dash.js \
ui/dateMenu.js \
@ -29,7 +37,9 @@ nobase_dist_js_DATA = \
ui/endSessionDialog.js \
ui/environment.js \
ui/extensionSystem.js \
ui/flashspot.js \
ui/iconGrid.js \
ui/keyboard.js \
ui/layout.js \
ui/lightbox.js \
ui/link.js \
@ -39,6 +49,8 @@ nobase_dist_js_DATA = \
ui/main.js \
ui/messageTray.js \
ui/modalDialog.js \
ui/networkAgent.js \
ui/shellEntry.js \
ui/shellMountOperation.js \
ui/notificationDaemon.js \
ui/overview.js \
@ -53,7 +65,6 @@ nobase_dist_js_DATA = \
ui/searchDisplay.js \
ui/shellDBus.js \
ui/statusIconDispatcher.js \
ui/statusMenu.js \
ui/status/accessibility.js \
ui/status/keyboard.js \
ui/status/network.js \
@ -62,7 +73,9 @@ nobase_dist_js_DATA = \
ui/status/bluetooth.js \
ui/telepathyClient.js \
ui/tweener.js \
ui/userMenu.js \
ui/viewSelector.js \
ui/wanda.js \
ui/windowAttentionHandler.js \
ui/windowManager.js \
ui/workspace.js \

279
js/extensionPrefs/main.js Normal file
View File

@ -0,0 +1,279 @@
const Lang = imports.lang;
const Gettext = imports.gettext;
const GLib = imports.gi.GLib;
const GObject = imports.gi.GObject;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Pango = imports.gi.Pango;
const _ = Gettext.gettext;
const Config = imports.misc.config;
const Format = imports.misc.format;
const ExtensionUtils = imports.misc.extensionUtils;
const GnomeShellIface = <interface name="org.gnome.Shell">
<signal name="ExtensionStatusChanged">
<arg type="s" name="uuid"/>
<arg type="i" name="state"/>
<arg type="s" name="error"/>
</signal>
</interface>;
const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface);
function stripPrefix(string, prefix) {
if (string.slice(0, prefix.length) == prefix)
return string.slice(prefix.length);
return string;
}
const Application = new Lang.Class({
Name: 'Application',
_init: function() {
GLib.set_prgname('gnome-shell-extension-prefs');
this.application = new Gtk.Application({
application_id: 'org.gnome.shell.ExtensionPrefs',
flags: Gio.ApplicationFlags.HANDLES_COMMAND_LINE
});
this.application.connect('activate', Lang.bind(this, this._onActivate));
this.application.connect('command-line', Lang.bind(this, this._onCommandLine));
this.application.connect('startup', Lang.bind(this, this._onStartup));
this._extensionPrefsModules = {};
this._extensionIters = {};
},
_buildModel: function() {
this._model = new Gtk.ListStore();
this._model.set_column_types([GObject.TYPE_STRING, GObject.TYPE_STRING]);
},
_extensionAvailable: function(uuid) {
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return false;
if (ExtensionUtils.isOutOfDate(extension))
return false;
if (!extension.dir.get_child('prefs.js').query_exists(null))
return false;
return true;
},
_setExtensionInsensitive: function(layout, cell, model, iter, data) {
let uuid = model.get_value(iter, 0);
if (!this._extensionAvailable(uuid))
cell.set_sensitive(false);
},
_getExtensionPrefsModule: function(extension) {
let uuid = extension.metadata.uuid;
if (this._extensionPrefsModules.hasOwnProperty(uuid))
return this._extensionPrefsModules[uuid];
ExtensionUtils.installImporter(extension);
let prefsModule = extension.imports.prefs;
prefsModule.init(extension.metadata);
this._extensionPrefsModules[uuid] = prefsModule;
return prefsModule;
},
_selectExtension: function(uuid) {
if (!this._extensionAvailable(uuid))
return;
let extension = ExtensionUtils.extensions[uuid];
let widget;
try {
let prefsModule = this._getExtensionPrefsModule(extension);
widget = prefsModule.buildPrefsWidget();
} catch (e) {
widget = this._buildErrorUI(extension, e);
}
// Destroy the current prefs widget, if it exists
if (this._extensionPrefsBin.get_child())
this._extensionPrefsBin.get_child().destroy();
this._extensionPrefsBin.add(widget);
this._extensionSelector.set_active_iter(this._extensionIters[uuid]);
},
_extensionSelected: function() {
let [success, iter] = this._extensionSelector.get_active_iter();
if (!success)
return;
let uuid = this._model.get_value(iter, 0);
this._selectExtension(uuid);
},
_buildErrorUI: function(extension, exc) {
let box = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
let label = new Gtk.Label({
label: _("There was an error loading the preferences dialog for %s:").format(extension.metadata.name)
});
box.add(label);
let errortext = '';
errortext += exc;
errortext += '\n\n';
errortext += 'Stack trace:\n';
// Indent stack trace.
errortext += exc.stack.split('\n').map(function(line) {
return ' ' + line;
}).join('\n');
let scroll = new Gtk.ScrolledWindow({ vexpand: true });
let buffer = new Gtk.TextBuffer({ text: errortext });
let textview = new Gtk.TextView({ buffer: buffer });
textview.override_font(Pango.font_description_from_string('monospace'));
scroll.add(textview);
box.add(scroll);
box.show_all();
return box;
},
_buildUI: function(app) {
this._window = new Gtk.ApplicationWindow({ application: app,
window_position: Gtk.WindowPosition.CENTER,
title: _("GNOME Shell Extension Preferences") });
this._window.set_size_request(600, 400);
let vbox = new Gtk.Box({ orientation: Gtk.Orientation.VERTICAL });
this._window.add(vbox);
let toolbar = new Gtk.Toolbar();
toolbar.get_style_context().add_class(Gtk.STYLE_CLASS_PRIMARY_TOOLBAR);
vbox.add(toolbar);
let toolitem;
let label = new Gtk.Label({ label: _("<b>Extension</b>"),
use_markup: true });
toolitem = new Gtk.ToolItem({ child: label });
toolbar.add(toolitem);
this._extensionSelector = new Gtk.ComboBox({ model: this._model,
margin_left: 8,
hexpand: true });
this._extensionSelector.get_style_context().add_class(Gtk.STYLE_CLASS_RAISED);
let renderer = new Gtk.CellRendererText();
this._extensionSelector.pack_start(renderer, true);
this._extensionSelector.add_attribute(renderer, 'text', 1);
this._extensionSelector.set_cell_data_func(renderer, Lang.bind(this, this._setExtensionInsensitive), null);
this._extensionSelector.connect('changed', Lang.bind(this, this._extensionSelected));
toolitem = new Gtk.ToolItem({ child: this._extensionSelector });
toolitem.set_expand(true);
toolbar.add(toolitem);
this._extensionPrefsBin = new Gtk.Frame();
vbox.add(this._extensionPrefsBin);
let label = new Gtk.Label({
label: _("Select an extension to configure using the combobox above."),
vexpand: true
});
this._extensionPrefsBin.add(label);
this._shellProxy = new GnomeShellProxy(Gio.DBus.session, 'org.gnome.Shell', '/org/gnome/Shell');
this._shellProxy.connectSignal('ExtensionStatusChanged', Lang.bind(this, function(proxy, senderName, [uuid, state, error]) {
if (ExtensionUtils.extensions[uuid] !== undefined)
this._scanExtensions();
}));
this._window.show_all();
},
_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) {
global.logError('' + e);
return;
}
let iter = this._model.append();
this._model.set(iter, [0, 1], [uuid, extension.metadata.name]);
this._extensionIters[uuid] = iter;
}));
},
_onActivate: function() {
this._window.present();
},
_onStartup: function(app) {
this._buildModel();
this._buildUI(app);
this._scanExtensions();
},
_onCommandLine: function(app, commandLine) {
app.activate();
let args = commandLine.get_arguments();
if (args.length) {
let uuid = args[0];
// Strip off "extension:///" prefix which fakes a URI, if it exists
uuid = stripPrefix(uuid, "extension:///");
if (!this._extensionAvailable(uuid))
return 1;
this._selectExtension(uuid);
}
return 0;
}
});
function initEnvironment() {
// Monkey-patch in a "global" object that fakes some Shell utilities
// that ExtensionUtils depends on.
window.global = {
log: function() {
print([].join.call(arguments, ', '));
},
logError: function(s) {
global.log('ERROR: ' + s);
},
userdatadir: GLib.build_filenamev([GLib.get_user_data_dir(), 'gnome-shell'])
};
String.prototype.format = Format.format;
}
function main(argv) {
initEnvironment();
ExtensionUtils.init();
Gettext.bindtextdomain(Config.GETTEXT_PACKAGE, Config.LOCALEDIR);
Gettext.textdomain(Config.GETTEXT_PACKAGE);
let app = new Application();
app.application.run(argv);
}

203
js/gdm/batch.js Normal file
View File

@ -0,0 +1,203 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/*
* Copyright 2011 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
const Lang = imports.lang;
const Signals = imports.signals;
const Task = new Lang.Class({
Name: 'Task',
_init: function(scope, handler) {
if (scope)
this.scope = scope;
else
this.scope = this;
this.handler = handler;
},
run: function() {
if (this.handler)
return this.handler.call(this.scope);
return null;
},
});
Signals.addSignalMethods(Task.prototype);
const Hold = new Lang.Class({
Name: 'Hold',
Extends: Task,
_init: function() {
this.parent(this, function () {
return this;
});
this._acquisitions = 1;
},
acquire: function() {
if (this._acquisitions <= 0)
throw new Error("Cannot acquire hold after it's been released");
this._acquisitions++;
},
acquireUntilAfter: function(hold) {
if (!hold.isAcquired())
return;
this.acquire();
let signalId = hold.connect('release', Lang.bind(this, function() {
hold.disconnect(signalId);
this.release();
}));
},
release: function() {
this._acquisitions--;
if (this._acquisitions == 0)
this.emit('release');
},
isAcquired: function() {
return this._acquisitions > 0;
}
});
Signals.addSignalMethods(Hold.prototype);
const Batch = new Lang.Class({
Name: 'Batch',
Extends: Task,
_init: function(scope, tasks) {
this.parent();
this.tasks = [];
for (let i = 0; i < tasks.length; i++) {
let task;
if (tasks[i] instanceof Task) {
task = tasks[i];
} else if (typeof tasks[i] == 'function') {
task = new Task(scope, tasks[i]);
} else {
throw new Error('Batch tasks must be functions or Task, Hold or Batch objects');
}
this.tasks.push(task);
}
},
process: function() {
throw new Error('Not implemented');
},
runTask: function() {
if (!(this._currentTaskIndex in this.tasks)) {
return null;
}
return this.tasks[this._currentTaskIndex].run();
},
_finish: function() {
this.hold.release();
},
nextTask: function() {
this._currentTaskIndex++;
// if the entire batch of tasks is finished, release
// the hold and notify anyone waiting on the batch
if (this._currentTaskIndex >= this.tasks.length) {
this._finish();
return;
}
this.process();
},
_start: function() {
// acquire a hold to get released when the entire
// batch of tasks is finished
this.hold = new Hold();
this._currentTaskIndex = 0;
this.process();
},
run: function() {
this._start();
// hold may be destroyed at this point
// if we're already done running
return this.hold;
},
cancel: function() {
this.tasks = this.tasks.splice(0, this._currentTaskIndex + 1);
}
});
Signals.addSignalMethods(Batch.prototype);
const ConcurrentBatch = new Lang.Class({
Name: 'ConcurrentBatch',
Extends: Batch,
process: function() {
let hold = this.runTask();
if (hold) {
this.hold.acquireUntilAfter(hold);
}
// Regardless of the state of the just run task,
// fire off the next one, so all the tasks can run
// concurrently.
this.nextTask();
}
});
Signals.addSignalMethods(ConcurrentBatch.prototype);
const ConsecutiveBatch = new Lang.Class({
Name: 'ConsecutiveBatch',
Extends: Batch,
process: function() {
let hold = this.runTask();
if (hold && hold.isAcquired()) {
// This task is inhibiting the batch. Wait on it
// before processing the next one.
let signalId = hold.connect('release',
Lang.bind(this, function() {
hold.disconnect(signalId);
this.nextTask();
}));
return;
} else {
// This task finished, process the next one
this.nextTask();
}
}
});
Signals.addSignalMethods(ConsecutiveBatch.prototype);

22
js/gdm/consoleKit.js Normal file
View File

@ -0,0 +1,22 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const ConsoleKitManagerIface = <interface name='org.freedesktop.ConsoleKit.Manager'>
<method name='CanRestart'>
<arg type='b' direction='out'/>
</method>
<method name='CanStop'>
<arg type='b' direction='out'/>
</method>
<method name='Restart' />
<method name='Stop' />
</interface>;
const ConsoleKitProxy = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);
function ConsoleKitManager() {
return new ConsoleKitProxy(Gio.DBus.system,
'org.freedesktop.ConsoleKit',
'/org/freedesktop/ConsoleKit/Manager');
};

20
js/gdm/fingerprint.js Normal file
View File

@ -0,0 +1,20 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const FprintManagerIface = <interface name='net.reactivated.Fprint.Manager'>
<method name='GetDefaultDevice'>
<arg type='o' direction='out' />
</method>
</interface>;
const FprintManagerProxy = Gio.DBusProxy.makeProxyWrapper(FprintManagerIface);
function FprintManager() {
return new FprintManagerProxy(Gio.DBus.system,
'net.reactivated.Fprint',
'/net/reactivated/Fprint/Manager');
};

1388
js/gdm/loginDialog.js Normal file

File diff suppressed because it is too large Load Diff

143
js/gdm/powerMenu.js Normal file
View File

@ -0,0 +1,143 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/*
* Copyright 2011 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
const Lang = imports.lang;
const UPowerGlib = imports.gi.UPowerGlib;
const ConsoleKit = imports.gdm.consoleKit;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const PowerMenuButton = new Lang.Class({
Name: 'PowerMenuButton',
Extends: PanelMenu.SystemStatusButton,
_init: function() {
this.parent('system-shutdown', null);
this._consoleKitManager = new ConsoleKit.ConsoleKitManager();
this._upClient = new UPowerGlib.Client();
this._createSubMenu();
this._upClient.connect('notify::can-suspend',
Lang.bind(this, this._updateHaveSuspend));
this._updateHaveSuspend();
// ConsoleKit doesn't send notifications when shutdown/reboot
// are disabled, so we update the menu item each time the menu opens
this.menu.connect('open-state-changed', Lang.bind(this,
function(menu, open) {
if (open) {
this._updateHaveShutdown();
this._updateHaveRestart();
}
}));
this._updateHaveShutdown();
this._updateHaveRestart();
},
_updateVisibility: function() {
if (!this._haveSuspend && !this._haveShutdown && !this._haveRestart)
this.actor.hide();
else
this.actor.show();
},
_updateHaveShutdown: function() {
this._consoleKitManager.CanStopRemote(Lang.bind(this,
function(result, error) {
if (!error)
this._haveShutdown = result;
else
this._haveShutdown = false;
if (this._haveShutdown) {
this._powerOffItem.actor.show();
} else {
this._powerOffItem.actor.hide();
}
this._updateVisibility();
}));
},
_updateHaveRestart: function() {
this._consoleKitManager.CanRestartRemote(Lang.bind(this,
function(result, error) {
if (!error)
this._haveRestart = result;
else
this._haveRestart = false;
if (this._haveRestart) {
this._restartItem.actor.show();
} else {
this._restartItem.actor.hide();
}
this._updateVisibility();
}));
},
_updateHaveSuspend: function() {
this._haveSuspend = this._upClient.get_can_suspend();
if (this._haveSuspend)
this._suspendItem.actor.show();
else
this._suspendItem.actor.hide();
this._updateVisibility();
},
_createSubMenu: function() {
let item;
item = new PopupMenu.PopupMenuItem(_("Suspend"));
item.connect('activate', Lang.bind(this, this._onActivateSuspend));
this.menu.addMenuItem(item);
this._suspendItem = item;
item = new PopupMenu.PopupMenuItem(_("Restart"));
item.connect('activate', Lang.bind(this, this._onActivateRestart));
this.menu.addMenuItem(item);
this._restartItem = item;
item = new PopupMenu.PopupMenuItem(_("Power Off"));
item.connect('activate', Lang.bind(this, this._onActivatePowerOff));
this.menu.addMenuItem(item);
this._powerOffItem = item;
},
_onActivateSuspend: function() {
if (this._haveSuspend)
this._upClient.suspend_sync(null);
},
_onActivateRestart: function() {
if (this._haveRestart)
this._consoleKitManager.RestartRemote();
},
_onActivatePowerOff: function() {
if (this._haveShutdown)
this._consoleKitManager.StopRemote();
}
});

View File

@ -1,4 +1,5 @@
/* mode: js2; indent-tabs-mode: nil; tab-size: 4 */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* The name of this package (not localized) */
const PACKAGE_NAME = '@PACKAGE_NAME@';
/* The version of this package */
@ -7,4 +8,9 @@ const PACKAGE_VERSION = '@PACKAGE_VERSION@';
const GJS_VERSION = '@GJS_VERSION@';
/* 1 if gnome-bluetooth is available, 0 otherwise */
const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;
/* The system TLS CA list */
const SHELL_SYSTEM_CA_FILE = '@SHELL_SYSTEM_CA_FILE@';
/* gettext package */
const GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@';
/* locale dir */
const LOCALEDIR = '@datadir@/locale';

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const St = imports.gi.St;
const Shell = imports.gi.Shell;
@ -8,11 +8,9 @@ const Search = imports.ui.search;
const THUMBNAIL_ICON_MARGIN = 2;
function DocInfo(recentInfo) {
this._init(recentInfo);
}
const DocInfo = new Lang.Class({
Name: 'DocInfo',
DocInfo.prototype = {
_init : function(recentInfo) {
this.recentInfo = recentInfo;
// We actually used get_modified() instead of get_visited()
@ -49,7 +47,7 @@ DocInfo.prototype = {
}
return mtype;
}
};
});
var docManagerInstance = null;
@ -62,11 +60,9 @@ function getDocManager() {
/**
* DocManager wraps the DocSystem, primarily to expose DocInfo objects.
*/
function DocManager() {
this._init();
}
const DocManager = new Lang.Class({
Name: 'DocManager',
DocManager.prototype = {
_init: function() {
this._docSystem = Shell.DocSystem.get_default();
this._infosByTimestamp = [];
@ -135,6 +131,6 @@ DocManager.prototype = {
return this._infosByUri[url];
})), terms);
}
};
});
Signals.addSignalMethods(DocManager.prototype);

194
js/misc/extensionUtils.js Normal file
View File

@ -0,0 +1,194 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
// Common utils for the extension system and the extension
// preferences tool
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const ShellJS = imports.gi.ShellJS;
const Config = imports.misc.config;
const ExtensionType = {
SYSTEM: 1,
PER_USER: 2
};
// GFile for user extensions
var userExtensionsDir = null;
// Maps uuid -> metadata object
const extensions = {};
function getCurrentExtension() {
let stack = (new Error()).stack;
// Assuming we're importing this directly from an extension (and we shouldn't
// ever not be), its UUID should be directly in the path here.
let extensionStackLine = stack.split('\n')[1];
if (!extensionStackLine)
throw new Error('Could not find current extension');
// The stack line is like:
// init([object Object])@/home/user/data/gnome-shell/extensions/u@u.id/prefs.js:8
//
// In the case that we're importing from
// module scope, the first field is blank:
// @/home/user/data/gnome-shell/extensions/u@u.id/prefs.js:8
let match = new RegExp('@(.+):\\d+').exec(extensionStackLine);
if (!match)
throw new Error('Could not find current extension');
let path = match[1];
let uuid = GLib.path_get_basename(GLib.path_get_dirname(path));
let extension = extensions[uuid];
if (extension === undefined)
throw new Error('Could not find current extension');
return extension;
}
/**
* versionCheck:
* @required: an array of versions we're compatible with
* @current: the version we have
*
* Check if a component is compatible for an extension.
* @required is an array, and at least one version must match.
* @current must be in the format <major>.<minor>.<point>.<micro>
* <micro> is always ignored
* <point> is ignored if <minor> is even (so you can target the
* whole stable release)
* <minor> and <major> must match
* Each target version must be at least <major> and <minor>
*/
function versionCheck(required, current) {
let currentArray = current.split('.');
let major = currentArray[0];
let minor = currentArray[1];
let point = currentArray[2];
for (let i = 0; i < required.length; i++) {
let requiredArray = required[i].split('.');
if (requiredArray[0] == major &&
requiredArray[1] == minor &&
(requiredArray[2] == point ||
(requiredArray[2] == undefined && parseInt(minor) % 2 == 0)))
return true;
}
return false;
}
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;
}
function createExtensionObject(uuid, dir, type) {
let info;
let metadataFile = dir.get_child('metadata.json');
if (!metadataFile.query_exists(null)) {
throw new Error('Missing metadata.json');
}
let metadataContents, success, tag;
try {
[success, metadataContents, tag] = metadataFile.load_contents(null);
} catch (e) {
throw new Error('Failed to load metadata.json: ' + e);
}
let meta;
try {
meta = JSON.parse(metadataContents);
} catch (e) {
throw new Error('Failed to parse metadata.json: ' + e);
}
let requiredProperties = ['uuid', 'name', 'description', 'shell-version'];
for (let i = 0; i < requiredProperties.length; i++) {
let prop = requiredProperties[i];
if (!meta[prop]) {
throw new Error('missing "' + prop + '" property in metadata.json');
}
}
// Encourage people to add this
if (!meta.url) {
global.log('Warning: Missing "url" property in metadata.json');
}
if (uuid != meta.uuid) {
throw new Error('uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + uuid + '"');
}
let extension = {};
extension.metadata = meta;
extension.uuid = meta.uuid;
extension.type = type;
extension.dir = dir;
extension.path = dir.get_path();
extension.error = '';
extension.hasPrefs = dir.get_child('prefs.js').query_exists(null);
extensions[uuid] = extension;
return extension;
}
var _extension = null;
function installImporter(extension) {
_extension = extension;
ShellJS.add_extension_importer('imports.misc.extensionUtils._extension', 'imports', extension.path);
_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) {
global.logError('' + e);
}
}
function scanExtensionsInDirectory(callback, dir, type) {
let fileEnum;
let file, info;
try {
fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
} catch(e) {
global.logError('' + e);
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);
}
function scanExtensions(callback) {
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))
scanExtensionsInDirectory(callback, dir, ExtensionType.SYSTEM);
}
scanExtensionsInDirectory(callback, userExtensionsDir, ExtensionType.PER_USER);
}

View File

@ -1,3 +1,5 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
@ -20,3 +22,25 @@ function listDirAsync(file, callback) {
enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, onNextFileComplete);
});
}
function deleteGFile(file) {
// Work around 'delete' being a keyword in JS.
return file['delete'](null);
}
function recursivelyDeleteDir(dir) {
let children = dir.enumerate_children('standard::name,standard::type',
Gio.FileQueryInfoFlags.NONE, null);
let info, child;
while ((info = children.next_file(null)) != null) {
let type = info.get_file_type();
let child = dir.get_child(info.get_name());
if (type == Gio.FileType.REGULAR)
deleteGFile(child);
else if (type == Gio.FileType.DIRECTORY)
recursivelyDeleteDir(child);
}
deleteGFile(dir);
}

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/*
* This function is intended to extend the String object and provide

View File

@ -1,20 +1,18 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const DBus = imports.dbus;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Signals = imports.signals;
const PresenceIface = {
name: 'org.gnome.SessionManager.Presence',
methods: [{ name: 'SetStatus',
inSignature: 'u',
outSignature: '' }],
properties: [{ name: 'status',
signature: 'u',
access: 'readwrite' }],
signals: [{ name: 'StatusChanged',
inSignature: 'u' }]
};
const PresenceIface = <interface name="org.gnome.SessionManager.Presence">
<method name="SetStatus">
<arg type="u" direction="in"/>
</method>
<property name="status" type="u" access="readwrite"/>
<signal name="StatusChanged">
<arg type="u" direction="out"/>
</signal>
</interface>;
const PresenceStatus = {
AVAILABLE: 0,
@ -23,104 +21,41 @@ const PresenceStatus = {
IDLE: 3
};
function Presence() {
this._init();
var PresenceProxy = Gio.DBusProxy.makeProxyWrapper(PresenceIface);
function Presence(initCallback, cancellable) {
return new PresenceProxy(Gio.DBus.session, 'org.gnome.SessionManager',
'/org/gnome/SessionManager/Presence', initCallback, cancellable);
}
Presence.prototype = {
_init: function() {
DBus.session.proxifyObject(this, 'org.gnome.SessionManager', '/org/gnome/SessionManager/Presence', this);
},
getStatus: function(callback) {
this.GetRemote('status', Lang.bind(this,
function(status, ex) {
if (!ex)
callback(this, status);
}));
},
setStatus: function(status) {
this.SetStatusRemote(status);
}
};
DBus.proxifyPrototype(Presence.prototype, PresenceIface);
// Note inhibitors are immutable objects, so they don't
// change at runtime (changes always come in the form
// of new inhibitors)
const InhibitorIface = {
name: 'org.gnome.SessionManager.Inhibitor',
properties: [{ name: 'app_id',
signature: 's',
access: 'readonly' },
{ name: 'client_id',
signature: 's',
access: 'readonly' },
{ name: 'reason',
signature: 's',
access: 'readonly' },
{ name: 'flags',
signature: 'u',
access: 'readonly' },
{ name: 'toplevel_xid',
signature: 'u',
access: 'readonly' },
{ name: 'cookie',
signature: 'u',
access: 'readonly' }],
};
const InhibitorIface = <interface name="org.gnome.SessionManager.Inhibitor">
<property name="app_id" type="s" access="read" />
<property name="client_id" type="s" access="read" />
<property name="reason" type="s" access="read" />
<property name="flags" type="u" access="read" />
<property name="toplevel_xid" type="u" access="read" />
<property name="cookie" type="u" access="read" />
</interface>;
function Inhibitor(objectPath) {
this._init(objectPath);
var InhibitorProxy = Gio.DBusProxy.makeProxyWrapper(InhibitorIface);
function Inhibitor(objectPath, initCallback, cancellable) {
return new InhibitorProxy(Gio.DBus.session, 'org.gnome.SessionManager', objectPath, initCallback, cancellable);
}
Inhibitor.prototype = {
_init: function(objectPath) {
DBus.session.proxifyObject(this,
"org.gnome.SessionManager",
objectPath);
this.isLoaded = false;
this._loadingPropertiesCount = InhibitorIface.properties.length;
for (let i = 0; i < InhibitorIface.properties.length; i++) {
let propertyName = InhibitorIface.properties[i].name;
this.GetRemote(propertyName, Lang.bind(this,
function(value, exception) {
if (exception)
return;
this[propertyName] = value;
this._loadingPropertiesCount--;
if (this._loadingPropertiesCount == 0) {
this.isLoaded = true;
this.emit("is-loaded");
}
}));
}
},
};
DBus.proxifyPrototype(Inhibitor.prototype, InhibitorIface);
Signals.addSignalMethods(Inhibitor.prototype);
// Not the full interface, only the methods we use
const SessionManagerIface = {
name: 'org.gnome.SessionManager',
methods: [
{ name: 'Logout', inSignature: 'u', outSignature: '' },
{ name: 'Shutdown', inSignature: '', outSignature: '' },
{ name: 'CanShutdown', inSignature: '', outSignature: 'b' }
]
};
const SessionManagerIface = <interface name="org.gnome.SessionManager">
<method name="Logout">
<arg type="u" direction="in" />
</method>
<method name="Shutdown" />
<method name="CanShutdown">
<arg type="b" direction="out" />
</method>
</interface>;
function SessionManager() {
this._init();
var SessionManagerProxy = Gio.DBusProxy.makeProxyWrapper(SessionManagerIface);
function SessionManager(initCallback, cancellable) {
return new SessionManagerProxy(Gio.DBus.session, 'org.gnome.SessionManager', '/org/gnome/SessionManager', initCallback, cancellable);
}
SessionManager.prototype = {
_init: function() {
DBus.session.proxifyObject(this, 'org.gnome.SessionManager', '/org/gnome/SessionManager');
}
};
DBus.proxifyPrototype(SessionManager.prototype, SessionManagerIface);

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Signals = imports.signals;
@ -7,11 +7,9 @@ const Params = imports.misc.params;
const DEFAULT_LIMIT = 512;
function HistoryManager(params) {
this._init(params);
}
const HistoryManager = new Lang.Class({
Name: 'HistoryManager',
HistoryManager.prototype = {
_init: function(params) {
params = Params.parse(params, { gsettingsKey: null,
limit: DEFAULT_LIMIT,
@ -111,5 +109,5 @@ HistoryManager.prototype = {
if (this._key)
global.settings.set_strv(this._key, this._history);
}
};
});
Signals.addSignalMethods(HistoryManager.prototype);

246
js/misc/jsParse.js Normal file
View File

@ -0,0 +1,246 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// Returns a list of potential completions for text. Completions either
// follow a dot (e.g. foo.ba -> bar) or they are picked from globalCompletionList (e.g. fo -> foo)
// commandHeader is prefixed on any expression before it is eval'ed. It will most likely
// consist of global constants that might not carry over from the calling environment.
//
// This function is likely the one you want to call from external modules
function getCompletions(text, commandHeader, globalCompletionList) {
let methods = [];
let expr, base;
let attrHead = '';
if (globalCompletionList == null) {
globalCompletionList = [];
}
let offset = getExpressionOffset(text, text.length - 1);
if (offset >= 0) {
text = text.slice(offset);
// Look for expressions like "Main.panel.foo" and match Main.panel and foo
let matches = text.match(/(.*)\.(.*)/);
if (matches) {
[expr, base, attrHead] = matches;
methods = getPropertyNamesFromExpression(base, commandHeader).filter(function(attr) {
return attr.slice(0, attrHead.length) == attrHead;
});
}
// Look for the empty expression or partially entered words
// not proceeded by a dot and match them against global constants
matches = text.match(/^(\w*)$/);
if (text == '' || matches) {
[expr, attrHead] = matches;
methods = globalCompletionList.filter(function(attr) {
return attr.slice(0, attrHead.length) == attrHead;
});
}
}
return [methods, attrHead];
}
//
// A few functions for parsing strings of javascript code.
//
// Identify characters that delimit an expression. That is,
// if we encounter anything that isn't a letter, '.', ')', or ']',
// we should stop parsing.
function isStopChar(c) {
return !c.match(/[\w\.\)\]]/);
}
// Given the ending position of a quoted string, find where it starts
function findMatchingQuote(expr, offset) {
let quoteChar = expr.charAt(offset);
for (let i = offset - 1; i >= 0; --i) {
if (expr.charAt(i) == quoteChar && expr.charAt(i-1) != '\\'){
return i;
}
}
return -1;
}
// Given the ending position of a regex, find where it starts
function findMatchingSlash(expr, offset) {
for (let i = offset - 1; i >= 0; --i) {
if (expr.charAt(i) == '/' && expr.charAt(i-1) != '\\'){
return i;
}
}
return -1;
}
// If expr.charAt(offset) is ')' or ']',
// return the position of the corresponding '(' or '[' bracket.
// This function does not check for syntactic correctness. e.g.,
// findMatchingBrace("[(])", 3) returns 1.
function findMatchingBrace(expr, offset) {
let closeBrace = expr.charAt(offset);
let openBrace = ({')': '(', ']': '['})[closeBrace];
function findTheBrace(expr, offset) {
if (offset < 0) {
return -1;
}
if (expr.charAt(offset) == openBrace) {
return offset;
}
if (expr.charAt(offset).match(/['"]/)) {
return findTheBrace(expr, findMatchingQuote(expr, offset) - 1);
}
if (expr.charAt(offset) == '/') {
return findTheBrace(expr, findMatchingSlash(expr, offset) - 1);
}
if (expr.charAt(offset) == closeBrace) {
return findTheBrace(expr, findTheBrace(expr, offset - 1) - 1);
}
return findTheBrace(expr, offset - 1);
}
return findTheBrace(expr, offset - 1);
}
// Walk expr backwards from offset looking for the beginning of an
// expression suitable for passing to eval.
// There is no guarantee of correct javascript syntax between the return
// value and offset. This function is meant to take a string like
// "foo(Obj.We.Are.Completing" and allow you to extract "Obj.We.Are.Completing"
function getExpressionOffset(expr, offset) {
while (offset >= 0) {
let currChar = expr.charAt(offset);
if (isStopChar(currChar)){
return offset + 1;
}
if (currChar.match(/[\)\]]/)) {
offset = findMatchingBrace(expr, offset);
}
--offset;
}
return offset + 1;
}
// Things with non-word characters or that start with a number
// are not accessible via .foo notation and so aren't returned
function isValidPropertyName(w) {
return !(w.match(/\W/) || w.match(/^\d/));
}
// To get all properties (enumerable and not), we need to walk
// the prototype chain ourselves
function getAllProps(obj) {
if (obj === null || obj === undefined) {
return [];
}
return Object.getOwnPropertyNames(obj).concat( getAllProps(Object.getPrototypeOf(obj)) );
}
// Given a string _expr_, returns all methods
// that can be accessed via '.' notation.
// e.g., expr="({ foo: null, bar: null, 4: null })" will
// return ["foo", "bar", ...] but the list will not include "4",
// since methods accessed with '.' notation must star with a letter or _.
function getPropertyNamesFromExpression(expr, commandHeader) {
if (commandHeader == null) {
commandHeader = '';
}
let obj = {};
if (!isUnsafeExpression(expr)) {
try {
obj = eval(commandHeader + expr);
} catch (e) {
return [];
}
} else {
return [];
}
let propsUnique = {};
if (typeof obj === 'object'){
let allProps = getAllProps(obj);
// Get only things we are allowed to complete following a '.'
allProps = allProps.filter( isValidPropertyName );
// Make sure propsUnique contains one key for every
// property so we end up with a unique list of properties
allProps.map(function(p){ propsUnique[p] = null; });
}
return Object.keys(propsUnique).sort();
}
// Given a list of words, returns the longest prefix they all have in common
function getCommonPrefix(words) {
let word = words[0];
for (let i = 0; i < word.length; i++) {
for (let w = 1; w < words.length; w++) {
if (words[w].charAt(i) != word.charAt(i))
return word.slice(0, i);
}
}
return word;
}
// Returns true if there is reason to think that eval(str)
// will modify the global scope
function isUnsafeExpression(str) {
// Remove any blocks that are quoted or are in a regex
function removeLiterals(str) {
if (str.length == 0) {
return '';
}
let currChar = str.charAt(str.length - 1);
if (currChar == '"' || currChar == '\'') {
return removeLiterals(str.slice(0, findMatchingQuote(str, str.length - 1)));
} else if (currChar == '/') {
return removeLiterals(str.slice(0, findMatchingSlash(str, str.length - 1)));
}
return removeLiterals(str.slice(0, str.length - 1)) + currChar;
}
// Check for any sort of assignment
// The strategy used is dumb: remove any quotes
// or regexs and comparison operators and see if there is an '=' character.
// If there is, it might be an unsafe assignment.
let prunedStr = removeLiterals(str);
prunedStr = prunedStr.replace(/[=!]==/g, ''); //replace === and !== with nothing
prunedStr = prunedStr.replace(/[=<>!]=/g, ''); //replace ==, <=, >=, != with nothing
if (prunedStr.match(/=/)) {
return true;
} else if (prunedStr.match(/;/)) {
// If we contain a semicolon not inside of a quote/regex, assume we're unsafe as well
return true;
}
return false;
}
// Returns a list of global keywords derived from str
function getDeclaredConstants(str) {
let ret = [];
str.split(';').forEach(function(s) {
let base, keyword;
let match = s.match(/const\s+(\w+)\s*=/);
if (match) {
[base, keyword] = match;
ret.push(keyword);
}
});
return ret;
}

View File

@ -1,6 +1,6 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const DBus = imports.dbus;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
@ -8,33 +8,43 @@ const Signals = imports.signals;
// The following are not the complete interfaces, just the methods we need
// (or may need in the future)
const ModemGsmNetworkInterface = {
name: 'org.freedesktop.ModemManager.Modem.Gsm.Network',
methods: [
{ name: 'GetRegistrationInfo', inSignature: '', outSignature: 'uss' },
{ name: 'GetSignalQuality', inSignature: '', outSignature: 'u' }
],
properties: [
{ name: 'AccessTechnology', signature: 'u', access: 'read' }
],
signals: [
{ name: 'SignalQuality', inSignature: 'u' },
{ name: 'RegistrationInfo', inSignature: 'uss' }
]
};
const ModemGsmNetworkProxy = DBus.makeProxyClass(ModemGsmNetworkInterface);
const ModemGsmNetworkInterface = <interface name="org.freedesktop.ModemManager.Modem.Gsm.Network">
<method name="GetRegistrationInfo">
<arg type="u" direction="out" />
<arg type="s" direction="out" />
<arg type="s" direction="out" />
</method>
<method name="GetSignalQuality">
<arg type="u" direction="out" />
</method>
<property name="AccessTechnology" type="u" access="read" />
<signal name="SignalQuality">
<arg type="u" direction="out" />
</signal>
<signal name="RegistrationInfo">
<arg type="u" direction="out" />
<arg type="s" direction="out" />
<arg type="s" direction="out" />
</signal>
</interface>;
const ModemCdmaInterface = {
name: 'org.freedesktop.ModemManager.Modem.Cdma',
methods: [
{ name: 'GetSignalQuality', inSignature: '', outSignature: 'u' },
{ name: 'GetServingSystem', inSignature: '', outSignature: 'usu' }
],
signals: [
{ name: 'SignalQuality', inSignature: 'u' }
]
};
const ModemCdmaProxy = DBus.makeProxyClass(ModemCdmaInterface);
const ModemGsmNetworkProxy = Gio.DBusProxy.makeProxyWrapper(ModemGsmNetworkInterface);
const ModemCdmaInterface = <interface name="org.freedesktop.ModemManager.Modem.Cdma">
<method name="GetSignalQuality">
<arg type="u" direction="out" />
</method>
<method name="GetServingSystem">
<arg type="u" direction="out" />
<arg type="s" direction="out" />
<arg type="u" direction="out" />
</method>
<signal name="SignalQuality">
<arg type="u" direction="out" />
</signal>
</interface>;
const ModemCdmaProxy = Gio.DBusProxy.makeProxyWrapper(ModemCdmaInterface);
let _providersTable;
function _getProvidersTable() {
@ -44,23 +54,21 @@ function _getProvidersTable() {
return _providersTable = providers;
}
function ModemGsm() {
this._init.apply(this, arguments);
}
const ModemGsm = new Lang.Class({
Name: 'ModemGsm',
ModemGsm.prototype = {
_init: function(path) {
this._proxy = new ModemGsmNetworkProxy(DBus.system, 'org.freedesktop.ModemManager', path);
this._proxy = new ModemGsmNetworkProxy(Gio.DBus.system, 'org.freedesktop.ModemManager', path);
this.signal_quality = 0;
this.operator_name = null;
// Code is duplicated because the function have different signatures
this._proxy.connect('SignalQuality', Lang.bind(this, function(proxy, quality) {
this._proxy.connectSignal('SignalQuality', Lang.bind(this, function(proxy, sender, [quality]) {
this.signal_quality = quality;
this.emit('notify::signal-quality');
}));
this._proxy.connect('RegistrationInfo', Lang.bind(this, function(proxy, status, code, name) {
this._proxy.connectSignal('RegistrationInfo', Lang.bind(this, function(proxy, sender, [status, code, name]) {
this.operator_name = this._findOperatorName(name, code);
this.emit('notify::operator-name');
}));
@ -146,21 +154,19 @@ ModemGsm.prototype = {
return name3 || name2 || null;
}
}
});
Signals.addSignalMethods(ModemGsm.prototype);
function ModemCdma() {
this._init.apply(this, arguments);
}
const ModemCdma = new Lang.Class({
Name: 'ModemCdma',
ModemCdma.prototype = {
_init: function(path) {
this._proxy = new ModemCdmaProxy(DBus.system, 'org.freedesktop.ModemManager', path);
this._proxy = new ModemCdmaProxy(Gio.DBus.system, 'org.freedesktop.ModemManager', path);
this.signal_quality = 0;
this.operator_name = null;
this._proxy.connect('SignalQuality', Lang.bind(this, function(proxy, quality) {
this.signal_quality = quality;
this._proxy.connectSignal('SignalQuality', Lang.bind(this, function(proxy, sender, params) {
this.signal_quality = params[0];
this.emit('notify::signal-quality');
// receiving this signal means the device got activated
@ -221,5 +227,5 @@ ModemCdma.prototype = {
return null;
}
};
});
Signals.addSignalMethods(ModemCdma.prototype);

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
// parse:
// @params: caller-provided parameter object, or %null

View File

@ -1,53 +1,48 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const DBus = imports.dbus;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const ScreenSaverIface = {
name: 'org.gnome.ScreenSaver',
methods: [{ name: 'GetActive',
inSignature: '',
outSignature: 'b' },
{ name: 'Lock',
inSignature: '' },
{ name: 'SetActive',
inSignature: 'b' }],
signals: [{ name: 'ActiveChanged',
inSignature: 'b' }]
};
const ScreenSaverIface = <interface name="org.gnome.ScreenSaver">
<method name="GetActive">
<arg type="b" direction="out" />
</method>
<method name="Lock" />
<method name="SetActive">
<arg type="b" direction="in" />
</method>
<signal name="ActiveChanged">
<arg type="b" direction="out" />
</signal>
</interface>;
const ScreenSaverInfo = Gio.DBusInterfaceInfo.new_for_xml(ScreenSaverIface);
function ScreenSaverProxy() {
this._init();
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
g_interface_name: ScreenSaverInfo.name,
g_interface_info: ScreenSaverInfo,
g_name: 'org.gnome.ScreenSaver',
g_object_path: '/org/gnome/ScreenSaver',
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
self.init(null);
self.screenSaverActive = false;
self.connectSignal('ActiveChanged', function(proxy, senderName, [isActive]) {
self.screenSaverActive = isActive;
});
self.connect('notify::g-name-owner', function() {
if (self.g_name_owner) {
self.GetActiveRemote(function(result, excp) {
if (result) {
let [isActive] = result;
self.screenSaverActive = isActive;
}
});
} else
self.screenSaverActive = false;
});
return self;
}
ScreenSaverProxy.prototype = {
_init: function() {
DBus.session.proxifyObject(this,
'org.gnome.ScreenSaver',
'/org/gnome/ScreenSaver');
DBus.session.watch_name('org.gnome.ScreenSaver',
false, // do not launch a name-owner if none exists
Lang.bind(this, this._onSSAppeared),
Lang.bind(this, this._onSSVanished));
this.screenSaverActive = false;
this.connect('ActiveChanged',
Lang.bind(this, this._onActiveChanged));
},
_onSSAppeared: function(owner) {
this.GetActiveRemote(Lang.bind(this, function(isActive) {
this.screenSaverActive = isActive;
}))
},
_onSSVanished: function(oldOwner) {
this.screenSaverActive = false;
},
_onActiveChanged: function(object, isActive) {
this.screenSaverActive = isActive;
}
};
DBus.proxifyPrototype(ScreenSaverProxy.prototype, ScreenSaverIface);

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gdk = imports.gi.Gdk;
const Gio = imports.gi.Gio;
@ -7,8 +7,32 @@ const Shell = imports.gi.Shell;
const Main = imports.ui.main;
/* http://daringfireball.net/2010/07/improved_regex_for_matching_urls */
const _urlRegexp = new RegExp('\\b(([a-z][\\w-]+:(/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}/)([^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+(\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:\'\\".,<>?«»“”‘’]))', 'gi');
// http://daringfireball.net/2010/07/improved_regex_for_matching_urls
const _balancedParens = '\\((?:[^\\s()<>]+|(?:\\(?:[^\\s()<>]+\\)))*\\)';
const _leadingJunk = '[\\s`(\\[{\'\\"<\u00AB\u201C\u2018]';
const _notTrailingJunk = '[^\\s`!()\\[\\]{};:\'\\".,<>?\u00AB\u00BB\u201C\u201D\u2018\u2019]';
const _urlRegexp = new RegExp(
'(^|' + _leadingJunk + ')' +
'(' +
'(?:' +
'[a-z][\\w-]+://' + // scheme://
'|' +
'www\\d{0,3}[.]' + // www.
'|' +
'[a-z0-9.\\-]+[.][a-z]{2,4}/' + // foo.xx/
')' +
'(?:' + // one or more:
'[^\\s()<>]+' + // run of non-space non-()
'|' + // or
_balancedParens + // balanced parens
')+' +
'(?:' + // end with:
_balancedParens + // balanced parens
'|' + // or
_notTrailingJunk + // last non-junk char
')' +
')', 'gi');
// findUrls:
// @str: string to find URLs in
@ -21,7 +45,7 @@ const _urlRegexp = new RegExp('\\b(([a-z][\\w-]+:(/{1,3}|[a-z0-9%])|www\\d{0,3}[
function findUrls(str) {
let res = [], match;
while ((match = _urlRegexp.exec(str)))
res.push({ url: match[0], pos: match.index });
res.push({ url: match[2], pos: match.index + match[1].length });
return res;
}
@ -208,3 +232,53 @@ function fixupPCIDescription(desc) {
return out.join(' ');
}
// lowerBound:
// @array: an array or array-like object, already sorted
// according to @cmp
// @val: the value to add
// @cmp: a comparator (or undefined to compare as numbers)
//
// Returns the position of the first element that is not
// lower than @val, according to @cmp.
// That is, returns the first position at which it
// is possible to insert @val without violating the
// order.
// This is quite like an ordinary binary search, except
// that it doesn't stop at first element comparing equal.
function lowerBound(array, val, cmp) {
let min, max, mid, v;
cmp = cmp || function(a, b) { return a - b; };
if (array.length == 0)
return 0;
min = 0; max = array.length;
while (min < (max - 1)) {
mid = Math.floor((min + max) / 2);
v = cmp(array[mid], val);
if (v < 0)
min = mid + 1;
else
max = mid;
}
return (min == max || cmp(array[min], val) < 0) ? max : min;
}
// insertSorted:
// @array: an array sorted according to @cmp
// @val: a value to insert
// @cmp: the sorting function
//
// Inserts @val into @array, preserving the
// sorting invariants.
// Returns the position at which it was inserted
function insertSorted(array, val, cmp) {
let pos = lowerBound(array, val, cmp);
array.splice(pos, 0, val);
return pos;
}

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Main = imports.ui.main;
const Scripting = imports.ui.scripting;
@ -113,10 +113,10 @@ function run() {
for (let i = 0; i < 2; i++) {
Scripting.scriptEvent('applicationsShowStart');
Main.overview.viewSelector.switchTab('applications');
Main.overview._viewSelector.switchTab('applications');
yield Scripting.waitLeisure();
Scripting.scriptEvent('applicationsShowDone');
Main.overview.viewSelector.switchTab('windows');
Main.overview._viewSelector.switchTab('windows');
yield Scripting.waitLeisure();
}
}

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gdk = imports.gi.Gdk;
@ -14,7 +14,7 @@ const Tweener = imports.ui.tweener;
const POPUP_APPICON_SIZE = 96;
const POPUP_SCROLL_TIME = 0.10; // seconds
const POPUP_FADE_IN_TIME = 0.4; // seconds
const POPUP_DELAY_TIMEOUT = 150; // milliseconds
const POPUP_FADE_OUT_TIME = 0.1; // seconds
const APP_ICON_HOVER_TIMEOUT = 200; // milliseconds
@ -31,11 +31,21 @@ function mod(a, b) {
return (a + b) % b;
}
function AltTabPopup() {
this._init();
function primaryModifier(mask) {
if (mask == 0)
return 0;
let primary = 1;
while (mask > 1) {
mask >>= 1;
primary <<= 1;
}
return primary;
}
AltTabPopup.prototype = {
const AltTabPopup = new Lang.Class({
Name: 'AltTabPopup',
_init : function() {
this.actor = new Shell.GenericContainer({ name: 'altTabPopup',
reactive: true,
@ -48,11 +58,13 @@ AltTabPopup.prototype = {
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._haveModal = false;
this._modifierMask = 0;
this._currentApp = 0;
this._currentWindow = -1;
this._thumbnailTimeoutId = 0;
this._motionTimeoutId = 0;
this._initialDelayTimeoutId = 0;
this.thumbnailsVisible = false;
@ -120,16 +132,50 @@ AltTabPopup.prototype = {
}
},
show : function(backward, binding) {
_getAppLists: function() {
let tracker = Shell.WindowTracker.get_default();
let apps = tracker.get_running_apps ('');
let appSys = Shell.AppSystem.get_default();
let allApps = appSys.get_running ();
if (!apps.length)
let screen = global.screen;
let display = screen.get_display();
let windows = display.get_tab_list(Meta.TabList.NORMAL, screen,
screen.get_active_workspace());
// windows is only the windows on the current workspace. For
// each one, if it corresponds to an app we know, move that
// app from allApps to apps.
let apps = [];
for (let i = 0; i < windows.length && allApps.length != 0; i++) {
let app = tracker.get_window_app(windows[i]);
let index = allApps.indexOf(app);
if (index != -1) {
apps.push(app);
allApps.splice(index, 1);
}
}
// Now @apps is a list of apps on the current workspace, in
// standard Alt+Tab order (MRU except for minimized windows),
// and allApps is a list of apps that only appear on other
// workspaces, sorted by user_time, which is good enough.
return [apps, allApps];
},
show : function(backward, binding, mask) {
let [localApps, otherApps] = this._getAppLists();
if (localApps.length == 0 && otherApps.length == 0)
return false;
if (!Main.pushModal(this.actor))
return false;
if (!Main.pushModal(this.actor)) {
// Probably someone else has a pointer grab, try again with keyboard only
if (!Main.pushModal(this.actor, global.get_current_time(), Meta.ModalOptions.POINTER_ALREADY_GRABBED)) {
return false;
}
}
this._haveModal = true;
this._modifierMask = primaryModifier(mask);
this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
@ -137,7 +183,7 @@ AltTabPopup.prototype = {
this.actor.connect('button-press-event', Lang.bind(this, this._clickedOutside));
this.actor.connect('scroll-event', Lang.bind(this, this._onScroll));
this._appSwitcher = new AppSwitcher(apps, this);
this._appSwitcher = new AppSwitcher(localApps, otherApps, this);
this.actor.add_actor(this._appSwitcher.actor);
this._appSwitcher.connect('item-activated', Lang.bind(this, this._appActivated));
this._appSwitcher.connect('item-entered', Lang.bind(this, this._appEntered));
@ -151,7 +197,7 @@ AltTabPopup.prototype = {
this.actor.get_allocation_box();
// Make the initial selection
if (binding == 'switch_group') {
if (binding == 'switch-group') {
if (backward) {
this._select(0, this._appIcons[0].cachedWindows.length - 1);
} else {
@ -160,9 +206,9 @@ AltTabPopup.prototype = {
else
this._select(0, 0);
}
} else if (binding == 'switch_group_backward') {
} else if (binding == 'switch-group-backward') {
this._select(0, this._appIcons[0].cachedWindows.length - 1);
} else if (binding == 'switch_windows_backward') {
} else if (binding == 'switch-windows-backward') {
this._select(this._appIcons.length - 1);
} else if (this._appIcons.length == 1) {
this._select(0);
@ -178,21 +224,18 @@ AltTabPopup.prototype = {
// details.) So we check now. (Have to do this after updating
// selection.)
let [x, y, mods] = global.get_pointer();
if (!(mods & Gdk.ModifierType.MOD1_MASK)) {
if (!(mods & this._modifierMask)) {
this._finish();
return false;
}
// Using easeInOutExpo over 400ms gives us 150ms of "nearly
// invisible" (less than 10% opacity), followed by a 100ms
// tween in (to 90% opacity, with the last 10% coming over the
// next 150ms). So if the user releases Alt quickly after we
// start tweening, they'll never see the switcher.
Tweener.addTween(this.actor,
{ opacity: 255,
time: POPUP_FADE_IN_TIME,
transition: 'easeInOutExpo'
});
// We delay showing the popup so that fast Alt+Tab users aren't
// disturbed by the popup briefly flashing.
this._initialDelayTimeoutId = Mainloop.timeout_add(POPUP_DELAY_TIMEOUT,
Lang.bind(this, function () {
this.actor.opacity = 255;
this._initialDelayTimeoutId = 0;
}));
return true;
},
@ -223,7 +266,7 @@ AltTabPopup.prototype = {
let keysym = event.get_key_symbol();
let event_state = Shell.get_event_state(event);
let backwards = event_state & Clutter.ModifierType.SHIFT_MASK;
let action = global.screen.get_display().get_keybinding_action(event.get_key_code(), event_state);
let action = global.display.get_keybinding_action(event.get_key_code(), event_state);
this._disableHover();
@ -258,7 +301,7 @@ AltTabPopup.prototype = {
_keyReleaseEvent : function(actor, event) {
let [x, y, mods] = global.get_pointer();
let state = mods & Clutter.ModifierType.MOD1_MASK;
let state = mods & this._modifierMask;
if (state == 0)
this._finish();
@ -395,6 +438,8 @@ AltTabPopup.prototype = {
Mainloop.source_remove(this._motionTimeoutId);
if (this._thumbnailTimeoutId != 0)
Mainloop.source_remove(this._thumbnailTimeoutId);
if (this._initialDelayTimeoutId != 0)
Mainloop.source_remove(this._initialDelayTimeoutId);
},
/**
@ -481,6 +526,10 @@ AltTabPopup.prototype = {
this.actor.add_actor(this._thumbnails.actor);
// Need to force an allocation so we can figure out whether we
// need to scroll when selecting
this._thumbnails.actor.get_allocation_box();
this._thumbnails.actor.opacity = 0;
Tweener.addTween(this._thumbnails.actor,
{ opacity: 255,
@ -489,13 +538,11 @@ AltTabPopup.prototype = {
onComplete: Lang.bind(this, function () { this.thumbnailsVisible = true; })
});
}
};
});
function SwitcherList(squareItems) {
this._init(squareItems);
}
const SwitcherList = new Lang.Class({
Name: 'SwitcherList',
SwitcherList.prototype = {
_init : function(squareItems) {
this.actor = new Shell.GenericContainer({ style_class: 'switcher-list' });
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
@ -632,11 +679,10 @@ SwitcherList.prototype = {
this._items[this._highlighted].add_style_pseudo_class('selected');
}
let monitor = Main.layoutManager.primaryMonitor;
let itemSize = this._items[index].allocation.x2 - this._items[index].allocation.x1;
let [posX, posY] = this._items[index].get_transformed_position();
posX += this.actor.x;
if (posX + itemSize > monitor.width + monitor.x)
let [absItemX, absItemY] = this._items[index].get_transformed_position();
let [result, posX, posY] = this.actor.transform_stage_point(absItemX, 0);
let [containerWidth, containerHeight] = this.actor.get_transformed_size();
if (posX + this._items[index].get_width() > containerWidth)
this._scrollToRight();
else if (posX < 0)
this._scrollToLeft();
@ -801,15 +847,13 @@ SwitcherList.prototype = {
// Clip the area for scrolling
this._clipBin.set_clip(0, -topPadding, (this.actor.allocation.x2 - this.actor.allocation.x1) - leftPadding - rightPadding, this.actor.height + bottomPadding);
}
};
});
Signals.addSignalMethods(SwitcherList.prototype);
function AppIcon(app) {
this._init(app);
}
const AppIcon = new Lang.Class({
Name: 'AppIcon',
AppIcon.prototype = {
_init: function(app) {
this.app = app;
this.actor = new St.BoxLayout({ style_class: 'alt-tab-app',
@ -827,35 +871,31 @@ AppIcon.prototype = {
this._iconBin.set_size(size, size);
this._iconBin.child = this.icon;
}
};
});
function AppSwitcher(apps, altTabPopup) {
this._init(apps, altTabPopup);
}
const AppSwitcher = new Lang.Class({
Name: 'AppSwitcher',
Extends: SwitcherList,
AppSwitcher.prototype = {
__proto__ : SwitcherList.prototype,
_init : function(localApps, otherApps, altTabPopup) {
this.parent(true);
_init : function(apps, altTabPopup) {
SwitcherList.prototype._init.call(this, true);
// Construct the AppIcons, sort by time, add to the popup
// Construct the AppIcons, add to the popup
let activeWorkspace = global.screen.get_active_workspace();
let workspaceIcons = [];
let otherIcons = [];
for (let i = 0; i < apps.length; i++) {
let appIcon = new AppIcon(apps[i]);
for (let i = 0; i < localApps.length; i++) {
let appIcon = new AppIcon(localApps[i]);
// Cache the window list now; we don't handle dynamic changes here,
// and we don't want to be continually retrieving it
appIcon.cachedWindows = appIcon.app.get_windows();
if (this._hasWindowsOnWorkspace(appIcon, activeWorkspace))
workspaceIcons.push(appIcon);
else
otherIcons.push(appIcon);
workspaceIcons.push(appIcon);
}
for (let i = 0; i < otherApps.length; i++) {
let appIcon = new AppIcon(otherApps[i]);
appIcon.cachedWindows = appIcon.app.get_windows();
otherIcons.push(appIcon);
}
workspaceIcons.sort(Lang.bind(this, this._sortAppIcon));
otherIcons.sort(Lang.bind(this, this._sortAppIcon));
this.icons = [];
this._arrows = [];
@ -917,7 +957,7 @@ AppSwitcher.prototype = {
_allocate: function (actor, box, flags) {
// Allocate the main list items
SwitcherList.prototype._allocate.call(this, actor, box, flags);
this.parent(actor, box, flags);
let arrowHeight = Math.floor(this.actor.get_theme_node().get_padding(St.Side.BOTTOM) / 3);
let arrowWidth = arrowHeight * 2;
@ -972,7 +1012,7 @@ AppSwitcher.prototype = {
this._arrows[this._curApp].remove_style_pseudo_class('highlighted');
}
SwitcherList.prototype.highlight.call(this, n, justOutline);
this.parent(n, justOutline);
this._curApp = n;
if (this._curApp != -1) {
@ -995,31 +1035,15 @@ AppSwitcher.prototype = {
if (appIcon.cachedWindows.length == 1)
arrow.hide();
},
_hasWindowsOnWorkspace: function(appIcon, workspace) {
let windows = appIcon.cachedWindows;
for (let i = 0; i < windows.length; i++) {
if (windows[i].get_workspace() == workspace)
return true;
}
return false;
},
_sortAppIcon : function(appIcon1, appIcon2) {
return appIcon1.app.compare(appIcon2.app);
}
};
});
function ThumbnailList(windows) {
this._init(windows);
}
ThumbnailList.prototype = {
__proto__ : SwitcherList.prototype,
const ThumbnailList = new Lang.Class({
Name: 'ThumbnailList',
Extends: SwitcherList,
_init : function(windows) {
SwitcherList.prototype._init.call(this);
this.parent(false);
let activeWorkspace = global.screen.get_active_workspace();
@ -1097,7 +1121,7 @@ ThumbnailList.prototype = {
// Make sure we only do this once
this._thumbnailBins = new Array();
}
};
});
function _drawArrow(area, side) {
let themeNode = area.get_theme_node();

View File

@ -1,8 +1,9 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const GMenu = imports.gi.GMenu;
const Shell = imports.gi.Shell;
const Lang = imports.lang;
const Signals = imports.signals;
@ -25,18 +26,15 @@ const MAX_APPLICATION_WORK_MILLIS = 75;
const MENU_POPUP_TIMEOUT = 600;
const SCROLL_TIME = 0.1;
function AlphabeticalView() {
this._init();
}
const AlphabeticalView = new Lang.Class({
Name: 'AlphabeticalView',
AlphabeticalView.prototype = {
_init: function() {
this._grid = new IconGrid.IconGrid({ xAlign: St.Align.START });
this._appSystem = Shell.AppSystem.get_default();
this._pendingAppLaterId = 0;
this._apps = [];
this._filterApp = null;
this._appIcons = {}; // desktop file id
let box = new St.BoxLayout({ vertical: true });
box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
@ -63,20 +61,17 @@ AlphabeticalView.prototype = {
_removeAll: function() {
this._grid.removeAll();
this._apps = [];
this._appIcons = {};
},
_addApp: function(appInfo) {
let appIcon = new AppWellIcon(this._appSystem.get_app(appInfo.get_id()));
_addApp: function(app) {
var id = app.get_id();
let appIcon = new AppWellIcon(app);
this._grid.addItem(appIcon.actor);
appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
appIcon._appInfo = appInfo;
if (this._filterApp && !this._filterApp(appInfo))
appIcon.actor.hide();
this._apps.push(appIcon);
this._appIcons[id] = appIcon;
},
_ensureIconVisible: function(icon) {
@ -105,60 +100,39 @@ AlphabeticalView.prototype = {
transition: 'easeOutQuad' });
},
setFilter: function(filter) {
this._filterApp = filter;
for (let i = 0; i < this._apps.length; i++)
this._apps[i].actor.visible = filter(this._apps[i]._appInfo);
},
// Create actors for the applications in an idle to avoid blocking
// for too long; see bug 647778
_addPendingApps: function() {
let i;
let startTimeMillis = new Date().getTime();
for (i = 0; i < this._pendingAppIds.length; i++) {
let id = this._pendingAppIds[i];
this._addApp(this._pendingApps[id]);
let currentTimeMillis = new Date().getTime();
if (currentTimeMillis - startTimeMillis > MAX_APPLICATION_WORK_MILLIS)
break;
}
this._pendingAppIds.splice(0, i + 1);
if (this._pendingAppIds.length > 0) {
return true;
setVisibleApps: function(apps) {
if (apps == null) { // null implies "all"
for (var id in this._appIcons) {
var icon = this._appIcons[id];
icon.actor.visible = true;
}
} else {
this._pendingAppLaterId = 0;
this._pendingAppIds = null;
this._pendingApps = null;
return false;
// Set everything to not-visible, then set to visible what we should see
for (var id in this._appIcons) {
var icon = this._appIcons[id];
icon.actor.visible = false;
}
for (var i = 0; i < apps.length; i++) {
var app = apps[i];
var id = app.get_id();
var icon = this._appIcons[id];
icon.actor.visible = true;
}
}
},
refresh: function(apps) {
let ids = [];
for (let i in apps)
ids.push(i);
ids.sort(function(a, b) {
return apps[a].get_name().localeCompare(apps[b].get_name());
});
setAppList: function(apps) {
this._removeAll();
this._pendingAppIds = ids;
this._pendingApps = apps;
if (this._pendingAppLaterId)
Meta.later_remove(this._pendingAppLaterId);
this._pendingAppLaterId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
Lang.bind(this, this._addPendingApps));
for (var i = 0; i < apps.length; i++) {
var app = apps[i];
this._addApp(app);
}
}
};
});
function ViewByCategories() {
this._init();
}
const ViewByCategories = new Lang.Class({
Name: 'ViewByCategories',
ViewByCategories.prototype = {
_init: function() {
this._appSystem = Shell.AppSystem.get_default();
this.actor = new St.BoxLayout({ style_class: 'all-app' });
@ -171,23 +145,24 @@ ViewByCategories.prototype = {
// -2 is a flag to indicate that nothing is selected
// (used only before the actor is mapped the first time)
this._currentCategory = -2;
this._filters = new St.BoxLayout({ vertical: true, reactive: true });
this._filtersBox = new St.ScrollView({ x_fill: false,
y_fill: false,
style_class: 'vfade' });
this._filtersBox.add_actor(this._filters);
this._categories = [];
this._apps = null;
this._categoryBox = new St.BoxLayout({ vertical: true, reactive: true });
this._categoryScroll = new St.ScrollView({ x_fill: false,
y_fill: false,
style_class: 'vfade' });
this._categoryScroll.add_actor(this._categoryBox);
this.actor.add(this._view.actor, { expand: true, x_fill: true, y_fill: true });
this.actor.add(this._filtersBox, { expand: false, y_fill: false, y_align: St.Align.START });
this.actor.add(this._categoryScroll, { expand: false, y_fill: false, y_align: St.Align.START });
// Always select the "All" filter when switching to the app view
this.actor.connect('notify::mapped', Lang.bind(this,
function() {
if (this.actor.mapped && this._allFilter)
if (this.actor.mapped && this._allCategoryButton)
this._selectCategory(-1);
}));
this._sections = [];
// We need a dummy actor to catch the keyboard focus if the
// user Ctrl-Alt-Tabs here before the deferred work creates
// our real contents
@ -201,64 +176,98 @@ ViewByCategories.prototype = {
this._currentCategory = num;
if (num != -1)
this._allFilter.remove_style_pseudo_class('selected');
else
this._allFilter.add_style_pseudo_class('selected');
if (num != -1) {
var category = this._categories[num];
this._allCategoryButton.remove_style_pseudo_class('selected');
this._view.setVisibleApps(category.apps);
} else {
this._allCategoryButton.add_style_pseudo_class('selected');
this._view.setVisibleApps(null);
}
this._view.setFilter(Lang.bind(this, function(app) {
if (num == -1)
return true;
return this._sections[num].name == app.get_section();
}));
for (let i = 0; i < this._sections.length; i++) {
for (var i = 0; i < this._categories.length; i++) {
if (i == num)
this._sections[i].filterActor.add_style_pseudo_class('selected');
this._categories[i].button.add_style_pseudo_class('selected');
else
this._sections[i].filterActor.remove_style_pseudo_class('selected');
this._categories[i].button.remove_style_pseudo_class('selected');
}
},
_addFilter: function(name, num) {
// Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
_loadCategory: function(dir, appList) {
var iter = dir.iter();
var nextType;
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
if (nextType == GMenu.TreeItemType.ENTRY) {
var entry = iter.get_entry();
var app = this._appSystem.lookup_app_by_tree_entry(entry);
if (!entry.get_app_info().get_nodisplay())
appList.push(app);
} else if (nextType == GMenu.TreeItemType.DIRECTORY) {
if (!dir.get_is_nodisplay())
this._loadCategory(iter.get_directory(), appList);
}
}
},
_addCategory: function(name, index, dir, allApps) {
let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
style_class: 'app-filter',
x_align: St.Align.START,
can_focus: true });
this._filters.add(button, { expand: true, x_fill: true, y_fill: false });
button.connect('clicked', Lang.bind(this, function() {
this._selectCategory(num);
this._selectCategory(index);
}));
if (num != -1)
this._sections[num] = { filterActor: button,
name: name };
else
this._allFilter = button;
var apps;
if (dir == null) {
apps = allApps;
this._allCategoryButton = button;
} else {
apps = [];
this._loadCategory(dir, apps);
this._categories.push({ apps: apps,
name: name,
button: button });
}
this._categoryBox.add(button, { expand: true, x_fill: true, y_fill: false });
},
_removeAll: function() {
this._sections = [];
this._filters.destroy_children();
this._categories = [];
this._categoryBox.destroy_children();
},
refresh: function(apps) {
refresh: function() {
this._removeAll();
let sections = this._appSystem.get_sections();
this._apps = apps;
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._addFilter(_("All"), -1);
this._addCategory(_("All"), -1, null, allApps);
if (!sections)
return;
var tree = this._appSystem.get_tree();
var root = tree.get_root_directory();
for (let i = 0; i < sections.length; i++)
this._addFilter(sections[i], i);
var iter = root.iter();
var nextType;
var i = 0;
while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
if (nextType == GMenu.TreeItemType.DIRECTORY) {
var dir = iter.get_directory();
if (dir.get_is_nodisplay())
continue;
this._addCategory(dir.get_name(), i, dir);
i++;
}
}
this._view.setAppList(allApps);
this._selectCategory(-1);
this._view.refresh(apps);
if (this._focusDummy) {
let focused = this._focusDummy.has_key_focus();
@ -268,16 +277,14 @@ ViewByCategories.prototype = {
this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
}
}
};
});
/* This class represents a display containing a collection of application items.
* The applications are sorted based on their name.
*/
function AllAppDisplay() {
this._init();
}
const AllAppDisplay = new Lang.Class({
Name: 'AllAppDisplay',
AllAppDisplay.prototype = {
_init: function() {
this._appSystem = Shell.AppSystem.get_default();
this._appSystem.connect('installed-changed', Lang.bind(this, function() {
@ -291,31 +298,22 @@ AllAppDisplay.prototype = {
},
_redisplay: function() {
let apps = this._appSystem.get_flattened_apps().filter(function(app) {
return !app.get_is_nodisplay();
});
this._appView.refresh(apps);
this._appView.refresh();
}
};
});
function BaseAppSearchProvider() {
this._init();
}
const AppSearchProvider = new Lang.Class({
Name: 'AppSearchProvider',
Extends: Search.SearchProvider,
BaseAppSearchProvider.prototype = {
__proto__: Search.SearchProvider.prototype,
_init: function() {
this.parent(_("APPLICATIONS"));
_init: function(name) {
Search.SearchProvider.prototype._init.call(this, name);
this._appSys = Shell.AppSystem.get_default();
},
getResultMeta: function(resultId) {
let app = this._appSys.get_app(resultId);
if (!app)
return null;
return { 'id': resultId,
getResultMeta: function(app) {
return { 'id': app,
'name': app.get_name(),
'createIcon': function(size) {
return app.create_icon_texture(size);
@ -323,105 +321,110 @@ BaseAppSearchProvider.prototype = {
};
},
activateResult: function(id, params) {
params = Params.parse(params, { workspace: null,
timestamp: null });
getInitialResultSet: function(terms) {
return this._appSys.initial_search(terms);
},
getSubsearchResultSet: function(previousResults, terms) {
return this._appSys.subsearch(previousResults, terms);
},
activateResult: function(app, params) {
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
let workspace = params.workspace ? params.workspace.index() : -1;
let event = Clutter.get_current_event();
let modifiers = event ? Shell.get_event_state(event) : 0;
let openNewWindow = modifiers & Clutter.ModifierType.CONTROL_MASK;
let app = this._appSys.get_app(id);
if (openNewWindow)
app.open_new_window(workspace);
app.open_new_window(params.workspace);
else
app.activate(workspace);
app.activate_full(params.workspace, params.timestamp);
},
dragActivateResult: function(id, params) {
params = Params.parse(params, { workspace: null,
timestamp: null });
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
let app = this._appSys.get_app(id);
app.open_new_window(params.workspace ? params.workspace.index() : -1);
}
};
function AppSearchProvider() {
this._init();
}
AppSearchProvider.prototype = {
__proto__: BaseAppSearchProvider.prototype,
_init: function() {
BaseAppSearchProvider.prototype._init.call(this, _("APPLICATIONS"));
},
getInitialResultSet: function(terms) {
return this._appSys.initial_search(false, terms);
},
getSubsearchResultSet: function(previousResults, terms) {
return this._appSys.subsearch(false, previousResults, terms);
let app = this._appSys.lookup_app(id);
app.open_new_window(workspace);
},
createResultActor: function (resultMeta, terms) {
let app = this._appSys.get_app(resultMeta['id']);
let app = resultMeta['id'];
let icon = new AppWellIcon(app);
return icon.actor;
}
};
});
function PrefsSearchProvider() {
this._init();
}
PrefsSearchProvider.prototype = {
__proto__: BaseAppSearchProvider.prototype,
const SettingsSearchProvider = new Lang.Class({
Name: 'SettingsSearchProvider',
Extends: Search.SearchProvider,
_init: function() {
BaseAppSearchProvider.prototype._init.call(this, _("SETTINGS"));
this.parent(_("SETTINGS"));
this._appSys = Shell.AppSystem.get_default();
this._gnomecc = this._appSys.lookup_app('gnome-control-center.desktop');
},
getResultMeta: function(pref) {
return { 'id': pref,
'name': pref.get_name(),
'createIcon': function(size) {
return pref.create_icon_texture(size);
}
};
},
getInitialResultSet: function(terms) {
return this._appSys.initial_search(true, terms);
return this._appSys.search_settings(terms);
},
getSubsearchResultSet: function(previousResults, terms) {
return this._appSys.subsearch(true, previousResults, terms);
return this._appSys.search_settings(terms);
},
activateResult: function(pref, params) {
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
pref.activate_full(params.workspace, params.timestamp);
},
dragActivateResult: function(pref, params) {
this.activateResult(pref, params);
},
createResultActor: function (resultMeta, terms) {
let app = resultMeta['id'];
let icon = new AppWellIcon(app);
return icon.actor;
}
};
});
function AppIcon(app, params) {
this._init(app, params);
}
AppIcon.prototype = {
__proto__: IconGrid.BaseIcon.prototype,
const AppIcon = new Lang.Class({
Name: 'AppIcon',
Extends: IconGrid.BaseIcon,
_init : function(app, params) {
this.app = app;
let label = this.app.get_name();
IconGrid.BaseIcon.prototype._init.call(this,
label,
params);
this.parent(label, params);
},
createIcon: function(iconSize) {
return this.app.create_icon_texture(iconSize);
}
};
});
function AppWellIcon(app, iconParams) {
this._init(app, iconParams);
}
const AppWellIcon = new Lang.Class({
Name: 'AppWellIcon',
AppWellIcon.prototype = {
_init : function(app, iconParams) {
_init : function(app, iconParams, onActivateOverride) {
this.app = app;
this.actor = new St.Button({ style_class: 'app-well-app',
reactive: true,
@ -436,6 +439,8 @@ AppWellIcon.prototype = {
this.actor.label_actor = this.icon.label;
// A function callback to override the default "app.activate()"; used by preferences
this._onActivateOverride = onActivateOverride;
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this.actor.connect('popup-menu', Lang.bind(this, this._onKeyboardPopupMenu));
@ -465,6 +470,7 @@ AppWellIcon.prototype = {
Lang.bind(this,
this._onStateChanged));
this._onStateChanged();
this.isMenuUp = false;
},
_onDestroy: function() {
@ -537,7 +543,7 @@ AppWellIcon.prototype = {
this._menu.connect('activate-window', Lang.bind(this, function (menu, window) {
this.activateWindow(window);
}));
this._menu.connect('popup', Lang.bind(this, function (menu, isPoppedUp) {
this._menu.connect('open-state-changed', Lang.bind(this, function (menu, isPoppedUp) {
if (!isPoppedUp)
this._onMenuPoppedDown();
}));
@ -546,8 +552,8 @@ AppWellIcon.prototype = {
this._menuManager.addMenu(this._menu);
}
this.isMenuUp = true;
this.actor.set_hover(true);
this.actor.show_tooltip();
this._menu.popup();
return false;
@ -563,30 +569,35 @@ AppWellIcon.prototype = {
_onMenuPoppedDown: function() {
this.actor.sync_hover();
this.isMenuUp = false;
},
_onActivate: function (event) {
this.emit('launching');
let modifiers = Shell.get_event_state(event);
if (modifiers & Clutter.ModifierType.CONTROL_MASK
&& this.app.state == Shell.AppState.RUNNING) {
this.app.open_new_window(-1);
if (this._onActivateOverride) {
this._onActivateOverride(event);
} else {
this.app.activate(-1);
if (modifiers & Clutter.ModifierType.CONTROL_MASK
&& this.app.state == Shell.AppState.RUNNING) {
this.app.open_new_window(-1);
} else {
this.app.activate();
}
}
Main.overview.hide();
},
shellWorkspaceLaunch : function(params) {
params = Params.parse(params, { workspace: null,
timestamp: null });
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
this.app.open_new_window(params.workspace ? params.workspace.index() : -1);
this.app.open_new_window(params.workspace);
},
getDragActor: function() {
return this.app.create_icon_texture(Main.overview.dash.iconSize);
return this.app.create_icon_texture(Main.overview.dashIconSize);
},
// Returns the original actor that should align with the actor
@ -594,22 +605,19 @@ AppWellIcon.prototype = {
getDragActorSource: function() {
return this.icon.icon;
}
};
});
Signals.addSignalMethods(AppWellIcon.prototype);
function AppIconMenu(source) {
this._init(source);
}
AppIconMenu.prototype = {
__proto__: PopupMenu.PopupMenu.prototype,
const AppIconMenu = new Lang.Class({
Name: 'AppIconMenu',
Extends: PopupMenu.PopupMenu,
_init: function(source) {
let side = St.Side.LEFT;
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
side = St.Side.RIGHT;
PopupMenu.PopupMenu.prototype._init.call(this, source.actor, 0.5, side, 0);
this.parent(source.actor, 0.5, side);
// We want to keep the item hovered while the menu is up
this.blockSourceEvents = true;
@ -617,7 +625,6 @@ AppIconMenu.prototype = {
this._source = source;
this.connect('activate', Lang.bind(this, this._onActivate));
this.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
this.actor.add_style_class_name('app-well-menu');
@ -650,17 +657,18 @@ AppIconMenu.prototype = {
item._window = windows[i];
}
if (windows.length > 0)
if (!this._source.app.is_window_backed()) {
if (windows.length > 0)
this._appendSeparator();
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
this._appendSeparator();
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
this._appendSeparator();
this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
: _("Add to Favorites"));
this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
: _("Add to Favorites"));
}
},
_appendSeparator: function () {
@ -680,14 +688,6 @@ AppIconMenu.prototype = {
this.open();
},
_onOpenStateChanged: function (menu, open) {
if (open) {
this.emit('popup', true);
} else {
this.emit('popup', false);
}
},
_onActivate: function (actor, child) {
if (child._window) {
let metaWindow = child._window;
@ -705,5 +705,5 @@ AppIconMenu.prototype = {
}
this.close();
}
};
});
Signals.addSignalMethods(AppIconMenu.prototype);

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Shell = imports.gi.Shell;
const Lang = imports.lang;
@ -6,11 +6,9 @@ const Signals = imports.signals;
const Main = imports.ui.main;
function AppFavorites() {
this._init();
}
const AppFavorites = new Lang.Class({
Name: 'AppFavorites',
AppFavorites.prototype = {
FAVORITE_APPS_KEY: 'favorite-apps',
_init: function() {
@ -28,7 +26,7 @@ AppFavorites.prototype = {
let ids = global.settings.get_strv(this.FAVORITE_APPS_KEY);
let appSys = Shell.AppSystem.get_default();
let apps = ids.map(function (id) {
return appSys.get_app(id);
return appSys.lookup_app(id);
}).filter(function (app) {
return app != null;
});
@ -65,7 +63,7 @@ AppFavorites.prototype = {
if (appId in this._favorites)
return false;
let app = Shell.AppSystem.get_default().get_app(appId);
let app = Shell.AppSystem.get_default().lookup_app(appId);
if (!app)
return false;
@ -84,9 +82,9 @@ AppFavorites.prototype = {
if (!this._addFavorite(appId, pos))
return;
let app = Shell.AppSystem.get_default().get_app(appId);
let app = Shell.AppSystem.get_default().lookup_app(appId);
Main.overview.shellInfo.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () {
Main.overview.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () {
this._removeFavorite(appId);
}));
},
@ -117,12 +115,12 @@ AppFavorites.prototype = {
if (!this._removeFavorite(appId))
return;
Main.overview.shellInfo.setMessage(_("%s has been removed from your favorites.").format(app.get_name()),
Lang.bind(this, function () {
Main.overview.setMessage(_("%s has been removed from your favorites.").format(app.get_name()),
Lang.bind(this, function () {
this._addFavorite(appId, pos);
}));
}
};
});
Signals.addSignalMethods(AppFavorites.prototype);
var appFavoritesInstance = null;

View File

@ -1,7 +1,6 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const DBus = imports.dbus;
const Mainloop = imports.mainloop;
const Gio = imports.gi.Gio;
const Params = imports.misc.params;
@ -16,69 +15,61 @@ const SETTING_ENABLE_AUTOMOUNT = 'automount';
const AUTORUN_EXPIRE_TIMEOUT_SECS = 10;
const ConsoleKitSessionIface = {
name: 'org.freedesktop.ConsoleKit.Session',
methods: [{ name: 'IsActive',
inSignature: '',
outSignature: 'b' }],
signals: [{ name: 'ActiveChanged',
inSignature: 'b' }]
};
const ConsoleKitSessionIface = <interface name="org.freedesktop.ConsoleKit.Session">
<method name="IsActive">
<arg type="b" direction="out" />
</method>
<signal name="ActiveChanged">
<arg type="b" direction="out" />
</signal>
</interface>;
const ConsoleKitSessionProxy = DBus.makeProxyClass(ConsoleKitSessionIface);
const ConsoleKitSessionProxy = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface);
const ConsoleKitManagerIface = {
name: 'org.freedesktop.ConsoleKit.Manager',
methods: [{ name: 'GetCurrentSession',
inSignature: '',
outSignature: 'o' }]
};
const ConsoleKitManagerIface = <interface name="org.freedesktop.ConsoleKit.Manager">
<method name="GetCurrentSession">
<arg type="o" direction="out" />
</method>
</interface>;
const ConsoleKitManagerInfo = Gio.DBusInterfaceInfo.new_for_xml(ConsoleKitManagerIface);
function ConsoleKitManager() {
this._init();
};
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.system,
g_interface_name: ConsoleKitManagerInfo.name,
g_interface_info: ConsoleKitManagerInfo,
g_name: 'org.freedesktop.ConsoleKit',
g_object_path: '/org/freedesktop/ConsoleKit/Manager',
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
ConsoleKitManager.prototype = {
_init: function() {
this.sessionActive = true;
self._updateSessionActive = function() {
if (self.g_name_owner) {
self.GetCurrentSessionRemote(function([session]) {
self._ckSession = new ConsoleKitSessionProxy(Gio.DBus.system, 'org.freedesktop.ConsoleKit', session);
DBus.system.proxifyObject(this,
'org.freedesktop.ConsoleKit',
'/org/freedesktop/ConsoleKit/Manager');
self._ckSession.connectSignal('ActiveChanged', function(object, senderName, [isActive]) {
self.sessionActive = isActive;
});
self._ckSession.IsActiveRemote(function([isActive]) {
self.sessionActive = isActive;
});
});
} else {
self.sessionActive = true;
}
};
self.connect('notify::g-name-owner',
Lang.bind(self, self._updateSessionActive));
DBus.system.watch_name('org.freedesktop.ConsoleKit',
false, // do not launch a name-owner if none exists
Lang.bind(this, this._onManagerAppeared),
Lang.bind(this, this._onManagerVanished));
},
_onManagerAppeared: function(owner) {
this.GetCurrentSessionRemote(Lang.bind(this, this._onCurrentSession));
},
_onManagerVanished: function(oldOwner) {
this.sessionActive = true;
},
_onCurrentSession: function(session) {
this._ckSession = new ConsoleKitSessionProxy(DBus.system, 'org.freedesktop.ConsoleKit', session);
this._ckSession.connect
('ActiveChanged', Lang.bind(this, function(object, isActive) {
this.sessionActive = isActive;
}));
this._ckSession.IsActiveRemote(Lang.bind(this, function(isActive) {
this.sessionActive = isActive;
}));
}
};
DBus.proxifyPrototype(ConsoleKitManager.prototype, ConsoleKitManagerIface);
function AutomountManager() {
this._init();
self._updateSessionActive();
self.init(null);
return self;
}
AutomountManager.prototype = {
const AutomountManager = new Lang.Class({
Name: 'AutomountManager',
_init: function() {
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
this._volumeQueue = [];
@ -86,9 +77,8 @@ AutomountManager.prototype = {
this.ckListener = new ConsoleKitManager();
this._ssProxy = new ScreenSaver.ScreenSaverProxy();
this._ssProxy.connect('ActiveChanged',
Lang.bind(this,
this._screenSaverActiveChanged));
this._ssProxy.connectSignal('ActiveChanged',
Lang.bind(this, this._screenSaverActiveChanged));
this._volumeMonitor = Gio.VolumeMonitor.get();
@ -111,7 +101,7 @@ AutomountManager.prototype = {
Mainloop.idle_add(Lang.bind(this, this._startupMountAll));
},
_screenSaverActiveChanged: function(object, isActive) {
_screenSaverActiveChanged: function(object, senderName, [isActive]) {
if (!isActive) {
this._volumeQueue.forEach(Lang.bind(this, function(volume) {
this._checkAndMountVolume(volume);
@ -209,10 +199,14 @@ AutomountManager.prototype = {
}
}
// Volume is already mounted, don't bother.
if (volume.get_mount())
return;
if (!this._settings.get_boolean(SETTING_ENABLE_AUTOMOUNT) ||
!volume.should_automount() ||
!volume.can_mount()) {
// allow the autorun to run anyway; this can happen if the
// allow the autorun to run anyway; this can happen if the
// mount gets added programmatically later, even if
// should_automount() or can_mount() are false, like for
// blank optical media.
@ -275,4 +269,4 @@ AutomountManager.prototype = {
return false;
});
}
}
});

View File

@ -1,7 +1,6 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const DBus = imports.dbus;
const Gio = imports.gi.Gio;
const St = imports.gi.St;
@ -23,8 +22,6 @@ const AutorunSetting = {
ASK: 3
};
const HOTPLUG_ICON_SIZE = 16;
// misc utils
function ignoreAutorunForMount(mount) {
let root = mount.get_root();
@ -64,31 +61,23 @@ function startAppForMount(app, mount) {
/******************************************/
const HotplugSnifferIface = {
name: 'org.gnome.Shell.HotplugSniffer',
methods: [{ name: 'SniffURI',
inSignature: 's',
outSignature: 'as' }]
};
const HotplugSnifferIface = <interface name="org.gnome.Shell.HotplugSniffer">
<method name="SniffURI">
<arg type="s" direction="in" />
<arg type="as" direction="out" />
</method>
</interface>;
const HotplugSniffer = function() {
this._init();
};
HotplugSniffer.prototype = {
_init: function() {
DBus.session.proxifyObject(this,
const HotplugSnifferProxy = Gio.DBusProxy.makeProxyWrapper(HotplugSnifferIface);
function HotplugSniffer() {
return new HotplugSnifferProxy(Gio.DBus.session,
'org.gnome.Shell.HotplugSniffer',
'/org/gnome/Shell/HotplugSniffer');
},
};
DBus.proxifyPrototype(HotplugSniffer.prototype, HotplugSnifferIface);
function ContentTypeDiscoverer(callback) {
this._init(callback);
}
ContentTypeDiscoverer.prototype = {
const ContentTypeDiscoverer = new Lang.Class({
Name: 'ContentTypeDiscoverer',
_init: function(callback) {
this._callback = callback;
},
@ -116,9 +105,8 @@ ContentTypeDiscoverer.prototype = {
let root = mount.get_root();
let hotplugSniffer = new HotplugSniffer();
hotplugSniffer.SniffURIRemote
(root.get_uri(), DBus.CALL_FLAG_START,
Lang.bind(this, function(contentTypes) {
hotplugSniffer.SniffURIRemote(root.get_uri(),
Lang.bind(this, function([contentTypes]) {
this._emitCallback(mount, contentTypes);
}));
}
@ -146,13 +134,11 @@ ContentTypeDiscoverer.prototype = {
this._callback(mount, apps, contentTypes);
}
}
});
function AutorunManager() {
this._init();
}
const AutorunManager = new Lang.Class({
Name: 'AutorunManager',
AutorunManager.prototype = {
_init: function() {
this._volumeMonitor = Gio.VolumeMonitor.get();
@ -269,22 +255,19 @@ AutorunManager.prototype = {
+ ': ' + e.toString());
}
},
}
});
function AutorunResidentSource() {
this._init();
}
AutorunResidentSource.prototype = {
__proto__: MessageTray.Source.prototype,
const AutorunResidentSource = new Lang.Class({
Name: 'AutorunResidentSource',
Extends: MessageTray.Source,
_init: function() {
MessageTray.Source.prototype._init.call(this, _('Removable Devices'));
this.parent(_("Removable Devices"));
this._mounts = [];
this._notification = new AutorunResidentNotification(this);
this._setSummaryIcon(this.createNotificationIcon(HOTPLUG_ICON_SIZE));
this._setSummaryIcon(this.createNotificationIcon());
},
addMount: function(mount, apps) {
@ -329,23 +312,19 @@ AutorunResidentSource.prototype = {
}
},
createNotificationIcon: function(iconSize) {
return new St.Icon ({ icon_name: 'drive-harddisk',
icon_size: iconSize ? iconSize : this.ICON_SIZE });
createNotificationIcon: function() {
return new St.Icon ({ icon_name: 'media-removable',
icon_type: St.IconType.FULLCOLOR,
icon_size: this.ICON_SIZE });
}
}
});
function AutorunResidentNotification(source) {
this._init(source);
}
AutorunResidentNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
const AutorunResidentNotification = new Lang.Class({
Name: 'AutorunResidentNotification',
Extends: MessageTray.Notification,
_init: function(source) {
MessageTray.Notification.prototype._init.call(this, source,
source.title, null,
{ customContent: true });
this.parent(source, source.title, null, { customContent: true });
// set the notification as resident
this.setResident(true);
@ -419,13 +398,11 @@ AutorunResidentNotification.prototype = {
return item;
},
}
});
function AutorunTransientDispatcher() {
this._init();
}
const AutorunTransientDispatcher = new Lang.Class({
Name: 'AutorunTransientDispatcher',
AutorunTransientDispatcher.prototype = {
_init: function() {
this._sources = [];
this._settings = new Gio.Settings({ schema: SETTINGS_SCHEMA });
@ -492,7 +469,7 @@ AutorunTransientDispatcher.prototype = {
let app = null;
if (setting == AutorunSetting.RUN) {
app = Gio.app_info_get_default_for_type(type, false);
app = Gio.app_info_get_default_for_type(contentTypes[0], false);
} else if (setting == AutorunSetting.FILES) {
app = Gio.app_info_get_default_for_type('inode/directory', false);
}
@ -516,46 +493,38 @@ AutorunTransientDispatcher.prototype = {
// destroy the notification source
source.destroy();
}
}
});
function AutorunTransientSource(mount, apps) {
this._init(mount, apps);
}
AutorunTransientSource.prototype = {
__proto__: MessageTray.Source.prototype,
const AutorunTransientSource = new Lang.Class({
Name: 'AutorunTransientSource',
Extends: MessageTray.Source,
_init: function(mount, apps) {
MessageTray.Source.prototype._init.call(this, mount.get_name());
this.parent(mount.get_name());
this.mount = mount;
this.apps = apps;
this._notification = new AutorunTransientNotification(this);
this._setSummaryIcon(this.createNotificationIcon(this.ICON_SIZE));
this._setSummaryIcon(this.createNotificationIcon());
// add ourselves as a source, and popup the notification
Main.messageTray.add(this);
this.notify(this._notification);
},
createNotificationIcon: function(iconSize) {
createNotificationIcon: function() {
return new St.Icon({ gicon: this.mount.get_icon(),
icon_size: iconSize ? iconSize : this.ICON_SIZE });
icon_size: this.ICON_SIZE });
}
}
});
function AutorunTransientNotification(source) {
this._init(source);
}
AutorunTransientNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
const AutorunTransientNotification = new Lang.Class({
Name: 'AutorunTransientNotification',
Extends: MessageTray.Notification,
_init: function(source) {
MessageTray.Notification.prototype._init.call(this, source,
source.title, null,
{ customContent: true });
this.parent(source, source.title, null, { customContent: true });
this._box = new St.BoxLayout({ style_class: 'hotplug-transient-box',
vertical: true });
@ -588,7 +557,7 @@ AutorunTransientNotification.prototype = {
let label = new St.Bin({ y_align: St.Align.MIDDLE,
child: new St.Label
({ text: _("Open with %s").format(app.get_display_name()) })
({ text: _("Open with %s").format(app.get_name()) })
});
box.add(label);
@ -630,5 +599,5 @@ AutorunTransientNotification.prototype = {
return button;
}
}
});

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
@ -21,11 +21,9 @@ const POPUP_ANIMATION_TIME = 0.15;
* placed. The arrow position may be controlled via setArrowOrigin().
*
*/
function BoxPointer(side, binProperties) {
this._init(side, binProperties);
}
const BoxPointer = new Lang.Class({
Name: 'BoxPointer',
BoxPointer.prototype = {
_init: function(arrowSide, binProperties) {
this._arrowSide = arrowSide;
this._arrowOrigin = 0;
@ -46,6 +44,7 @@ BoxPointer.prototype = {
this._yOffset = 0;
this._xPosition = 0;
this._yPosition = 0;
this._sourceAlignment = 0.5;
},
show: function(animate, onComplete) {
@ -75,7 +74,7 @@ BoxPointer.prototype = {
Tweener.addTween(this, { opacity: 255,
xOffset: 0,
yOffset: 0,
transition: "linear",
transition: 'linear',
onComplete: onComplete,
time: POPUP_ANIMATION_TIME });
},
@ -106,7 +105,7 @@ BoxPointer.prototype = {
Tweener.addTween(this, { opacity: 0,
xOffset: xOffset,
yOffset: yOffset,
transition: "linear",
transition: 'linear',
time: POPUP_ANIMATION_TIME,
onComplete: Lang.bind(this, function () {
this.actor.hide();
@ -180,7 +179,7 @@ BoxPointer.prototype = {
this.bin.allocate(childBox, flags);
if (this._sourceActor && this._sourceActor.mapped)
this._reposition(this._sourceActor, this._gap, this._alignment);
this._reposition(this._sourceActor, this._arrowAlignment);
},
_drawBorder: function(area) {
@ -306,31 +305,43 @@ BoxPointer.prototype = {
cr.stroke();
},
setPosition: function(sourceActor, gap, alignment) {
setPosition: function(sourceActor, alignment) {
// We need to show it now to force an allocation,
// so that we can query the correct size.
this.actor.show();
this._sourceActor = sourceActor;
this._gap = gap;
this._alignment = alignment;
this._arrowAlignment = alignment;
this._reposition(sourceActor, gap, alignment);
this._reposition(sourceActor, alignment);
},
_reposition: function(sourceActor, gap, alignment) {
setSourceAlignment: function(alignment) {
this._sourceAlignment = alignment;
if (!this._sourceActor)
return;
// We need to show it now to force an allocation,
// so that we can query the correct size.
this.actor.show();
this._reposition(this._sourceActor, this._arrowAlignment);
},
_reposition: function(sourceActor, alignment) {
// Position correctly relative to the sourceActor
let sourceNode = sourceActor.get_theme_node();
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
let sourceAllocation = Shell.util_get_transformed_allocation(sourceActor);
let sourceCenterX = sourceAllocation.x1 + sourceContentBox.x1 + (sourceContentBox.x2 - sourceContentBox.x1) / 2;
let sourceCenterY = sourceAllocation.y1 + sourceContentBox.y1 + (sourceContentBox.y2 - sourceContentBox.y1) / 2;
let sourceCenterX = sourceAllocation.x1 + sourceContentBox.x1 + (sourceContentBox.x2 - sourceContentBox.x1) * this._sourceAlignment;
let sourceCenterY = sourceAllocation.y1 + sourceContentBox.y1 + (sourceContentBox.y2 - sourceContentBox.y1) * this._sourceAlignment;
let [minWidth, minHeight, natWidth, natHeight] = this.actor.get_preferred_size();
// We also want to keep it onscreen, and separated from the
// edge by the same distance as the main part of the box is
// separated from its sourceActor
let primary = Main.layoutManager.primaryMonitor;
let monitor = Main.layoutManager.findMonitorForActor(sourceActor);
let themeNode = this.actor.get_theme_node();
let borderWidth = themeNode.get_length('-arrow-border-width');
let arrowBase = themeNode.get_length('-arrow-base');
@ -338,6 +349,9 @@ BoxPointer.prototype = {
let margin = (4 * borderRadius + borderWidth + arrowBase);
let halfMargin = margin / 2;
let themeNode = this.actor.get_theme_node();
let gap = themeNode.get_length('-boxpointer-gap');
let resX, resY;
switch (this._arrowSide) {
@ -362,8 +376,8 @@ BoxPointer.prototype = {
case St.Side.BOTTOM:
resX = sourceCenterX - (halfMargin + (natWidth - margin) * alignment);
resX = Math.max(resX, primary.x + 10);
resX = Math.min(resX, primary.x + primary.width - (10 + natWidth));
resX = Math.max(resX, monitor.x + 10);
resX = Math.min(resX, monitor.x + monitor.width - (10 + natWidth));
this.setArrowOrigin(sourceCenterX - resX);
break;
@ -371,8 +385,8 @@ BoxPointer.prototype = {
case St.Side.RIGHT:
resY = sourceCenterY - (halfMargin + (natHeight - margin) * alignment);
resY = Math.max(resY, primary.y + 10);
resY = Math.min(resY, primary.y + primary.height - (10 + natHeight));
resY = Math.max(resY, monitor.y + 10);
resY = Math.min(resY, monitor.y + monitor.height - (10 + natHeight));
this.setArrowOrigin(sourceCenterY - resY);
break;
@ -436,4 +450,4 @@ BoxPointer.prototype = {
get opacity() {
return this.actor.opacity;
}
};
});

View File

@ -1,6 +1,5 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const DBus = imports.dbus;
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
@ -156,28 +155,24 @@ function _getEventDayAbbreviation(dayNumber) {
// Abstraction for an appointment/event in a calendar
function CalendarEvent(date, end, summary, allDay) {
this._init(date, end, summary, allDay);
}
const CalendarEvent = new Lang.Class({
Name: 'CalendarEvent',
CalendarEvent.prototype = {
_init: function(date, end, summary, allDay) {
this.date = date;
this.end = end;
this.summary = summary;
this.allDay = allDay;
}
};
});
// Interface for appointments/events - e.g. the contents of a calendar
//
// First, an implementation with no events
function EmptyEventSource() {
this._init();
}
const EmptyEventSource = new Lang.Class({
Name: 'EmptyEventSource',
EmptyEventSource.prototype = {
_init: function() {
},
@ -192,33 +187,32 @@ EmptyEventSource.prototype = {
hasEvents: function(day) {
return false;
}
};
});
Signals.addSignalMethods(EmptyEventSource.prototype);
const CalendarServerIface = {
name: 'org.gnome.Shell.CalendarServer',
methods: [{ name: 'GetEvents',
inSignature: 'xxb',
outSignature: 'a(sssbxxa{sv})' }],
signals: [{ name: 'Changed',
inSignature: '' }]
};
const CalendarServerIface = <interface name="org.gnome.Shell.CalendarServer">
<method name="GetEvents">
<arg type="x" direction="in" />
<arg type="x" direction="in" />
<arg type="b" direction="in" />
<arg type="a(sssbxxa{sv})" direction="out" />
</method>
<signal name="Changed" />
</interface>;
const CalendarServer = function () {
this._init();
};
const CalendarServerInfo = Gio.DBusInterfaceInfo.new_for_xml(CalendarServerIface);
CalendarServer.prototype = {
_init: function() {
DBus.session.proxifyObject(this, 'org.gnome.Shell.CalendarServer', '/org/gnome/Shell/CalendarServer');
}
};
function CalendarServer() {
var self = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
g_interface_name: CalendarServerInfo.name,
g_interface_info: CalendarServerInfo,
g_name: 'org.gnome.Shell.CalendarServer',
g_object_path: '/org/gnome/Shell/CalendarServer',
g_flags: (Gio.DBusProxyFlags.DO_NOT_AUTO_START |
Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES) });
DBus.proxifyPrototype(CalendarServer.prototype, CalendarServerIface);
// an implementation that reads data from a session bus service
function DBusEventSource(owner) {
this._init(owner);
self.init(null);
return self;
}
function _datesEqual(a, b) {
@ -239,18 +233,22 @@ function _dateIntervalsOverlap(a0, a1, b0, b1)
return true;
}
// an implementation that reads data from a session bus service
const DBusEventSource = new Lang.Class({
Name: 'DBusEventSource',
DBusEventSource.prototype = {
_init: function(owner) {
_init: function() {
this._resetCache();
this._dbusProxy = new CalendarServer(owner);
this._dbusProxy.connect('Changed', Lang.bind(this, this._onChanged));
this._dbusProxy = new CalendarServer();
this._dbusProxy.connectSignal('Changed', Lang.bind(this, this._onChanged));
DBus.session.watch_name('org.gnome.Shell.CalendarServer',
false, // do not launch a name-owner if none exists
Lang.bind(this, this._onNameAppeared),
Lang.bind(this, this._onNameVanished));
this._dbusProxy.connect('notify::g-name-owner', Lang.bind(this, function() {
if (this._dbusProxy.g_name_owner)
this._onNameAppeared();
else
this._onNameVanished();
}));
},
_resetCache: function() {
@ -273,7 +271,7 @@ DBusEventSource.prototype = {
this._loadEvents(false);
},
_onEventsReceived: function(appointments) {
_onEventsReceived: function([appointments]) {
let newEvents = [];
if (appointments != null) {
for (let n = 0; n < appointments.length; n++) {
@ -296,9 +294,9 @@ DBusEventSource.prototype = {
_loadEvents: function(forceReload) {
if (this._curRequestBegin && this._curRequestEnd){
let callFlags = 0;
let callFlags = Gio.DBusCallFlags.NO_AUTO_START;
if (forceReload)
callFlags |= DBus.CALL_FLAG_START;
callFlags = Gio.DBusCallFlags.NONE;
this._dbusProxy.GetEventsRemote(this._curRequestBegin.getTime() / 1000,
this._curRequestEnd.getTime() / 1000,
forceReload,
@ -339,29 +337,26 @@ DBusEventSource.prototype = {
return true;
}
};
});
Signals.addSignalMethods(DBusEventSource.prototype);
// Calendar:
// @eventSource: is an object implementing the EventSource API, e.g. the
// requestRange(), getEvents(), hasEvents() methods and the ::changed signal.
function Calendar(eventSource) {
this._init(eventSource);
}
const Calendar = new Lang.Class({
Name: 'Calendar',
Calendar.prototype = {
_init: function(eventSource) {
this._eventSource = eventSource;
if (eventSource) {
this._eventSource = eventSource;
this._eventSource.connect('changed', Lang.bind(this,
function() {
this._update(false);
}));
this._eventSource.connect('changed', Lang.bind(this,
function() {
this._update(false);
}));
}
// FIXME: This is actually the fallback method for GTK+ for the week start;
// GTK+ by preference uses nl_langinfo (NL_TIME_FIRST_WEEKDAY). We probably
// should add a C function so we can do the full handling.
this._weekStart = NaN;
this._weekStart = Shell.util_get_week_start();
this._weekdate = NaN;
this._digitWidth = NaN;
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.calendar' });
@ -369,16 +364,6 @@ Calendar.prototype = {
this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange));
this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY);
let weekStartString = Gettext_gtk30.gettext('calendar:week_start:0');
if (weekStartString.indexOf('calendar:week_start:') == 0) {
this._weekStart = parseInt(weekStartString.substring(20));
}
if (isNaN(this._weekStart) || this._weekStart < 0 || this._weekStart > 6) {
log('Translation of "calendar:week_start:0" in GTK+ is not correct');
this._weekStart = 0;
}
// Find the ordering for month/year in the calendar heading
this._headerFormatWithoutYear = '%B';
switch (Gettext_gtk30.gettext('calendar:MY')) {
@ -567,13 +552,16 @@ Calendar.prototype = {
while (true) {
let button = new St.Button({ label: iter.getDate().toString() });
if (!this._eventSource)
button.reactive = false;
let iterStr = iter.toUTCString();
button.connect('clicked', Lang.bind(this, function() {
let newlySelectedDate = new Date(iterStr);
this.setDate(newlySelectedDate, false);
}));
let hasEvents = this._eventSource.hasEvents(iter);
let hasEvents = this._eventSource && this._eventSource.hasEvents(iter);
let styleClass = 'calendar-day-base calendar-day';
if (_isWorkDay(iter))
styleClass += ' calendar-work-day'
@ -620,17 +608,16 @@ Calendar.prototype = {
}
// Signal to the event source that we are interested in events
// only from this date range
this._eventSource.requestRange(beginDate, iter, forceReload);
if (this._eventSource)
this._eventSource.requestRange(beginDate, iter, forceReload);
}
};
});
Signals.addSignalMethods(Calendar.prototype);
function EventsList(eventSource) {
this._init(eventSource);
}
const EventsList = new Lang.Class({
Name: 'EventsList',
EventsList.prototype = {
_init: function(eventSource) {
this.actor = new St.BoxLayout({ vertical: true, style_class: 'events-header-vbox'});
this._date = new Date();
@ -638,17 +625,7 @@ EventsList.prototype = {
this._eventSource.connect('changed', Lang.bind(this, this._update));
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
this._desktopSettings.connect('changed', Lang.bind(this, this._update));
let weekStartString = Gettext_gtk30.gettext('calendar:week_start:0');
if (weekStartString.indexOf('calendar:week_start:') == 0) {
this._weekStart = parseInt(weekStartString.substring(20));
}
if (isNaN(this._weekStart) ||
this._weekStart < 0 ||
this._weekStart > 6) {
log('Translation of "calendar:week_start:0" in GTK+ is not correct');
this._weekStart = 0;
}
this._weekStart = Shell.util_get_week_start();
this._update();
},
@ -667,6 +644,9 @@ EventsList.prototype = {
},
_addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
if (!this._eventSource)
return;
let events = this._eventSource.getEvents(begin, end);
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;
@ -768,4 +748,4 @@ EventsList.prototype = {
this._showOtherDay(this._date);
}
}
};
});

View File

@ -1,441 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const Main = imports.ui.main;
const Params = imports.misc.params;
const ScreenSaver = imports.misc.screenSaver;
// This manages the shell "chrome"; the UI that's visible in the
// normal mode (ie, outside the Overview), that surrounds the main
// workspace content.
const defaultParams = {
visibleInFullscreen: false,
affectsStruts: true,
affectsInputRegion: true
};
function Chrome() {
this._init();
}
Chrome.prototype = {
_init: function() {
// The group itself has zero size so it doesn't interfere with DND
this.actor = new Shell.GenericContainer({ width: 0, height: 0 });
Main.uiGroup.add_actor(this.actor);
this.actor.connect('allocate', Lang.bind(this, this._allocated));
this._monitors = [];
this._inOverview = false;
this._trackedActors = [];
Main.layoutManager.connect('monitors-changed',
Lang.bind(this, this._relayout));
global.screen.connect('restacked',
Lang.bind(this, this._windowsRestacked));
// Need to update struts on new workspaces when they are added
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._queueUpdateRegions));
Main.overview.connect('showing',
Lang.bind(this, this._overviewShowing));
Main.overview.connect('hidden',
Lang.bind(this, this._overviewHidden));
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
this._screenSaverProxy.connect('ActiveChanged', Lang.bind(this, this._onScreenSaverActiveChanged));
this._screenSaverProxy.GetActiveRemote(Lang.bind(this,
function(result, err) {
if (!err)
this._onScreenSaverActiveChanged(this._screenSaverProxy, result);
}));
this._relayout();
},
_allocated: function(actor, box, flags) {
let children = this.actor.get_children();
for (let i = 0; i < children.length; i++)
children[i].allocate_preferred_size(flags);
},
// addActor:
// @actor: an actor to add to the chrome layer
// @params: (optional) additional params
//
// Adds @actor to the chrome layer and extends the input region
// and window manager struts to include it. (Window manager struts
// will only be affected if @actor is touching a screen edge.)
// Changes in @actor's size and position will automatically result
// in appropriate changes to the input region and struts. Changes
// in its visibility will affect the input region, but NOT the
// struts.
//
// If %visibleInFullscreen is %true, the actor will be visible
// even when a fullscreen window should be covering it.
//
// If %affectsStruts or %affectsInputRegion is %false, the actor
// will not have the indicated effect.
addActor: function(actor, params) {
this.actor.add_actor(actor);
this._trackActor(actor, params);
},
// trackActor:
// @actor: a descendant of the chrome to begin tracking
// @params: parameters describing how to track @actor
//
// Tells the chrome to track @actor, which must be a descendant
// of an actor added via addActor(). This can be used to extend the
// struts or input region to cover specific children.
//
// @params can have any of the same values as in addActor(), though
// some possibilities don't make sense (eg, trying to have a
// %visibleInFullscreen child of a non-%visibleInFullscreen parent).
// By default, @actor has the same params as its chrome ancestor.
trackActor: function(actor, params) {
let ancestor = actor.get_parent();
let index = this._findActor(ancestor);
while (ancestor && index == -1) {
ancestor = ancestor.get_parent();
index = this._findActor(ancestor);
}
if (!ancestor)
throw new Error('actor is not a descendent of the chrome layer');
let ancestorData = this._trackedActors[index];
if (!params)
params = {};
// We can't use Params.parse here because we want to drop
// the extra values like ancestorData.actor
for (let prop in defaultParams) {
if (!params[prop])
params[prop] = ancestorData[prop];
}
this._trackActor(actor, params);
},
// untrackActor:
// @actor: an actor previously tracked via trackActor()
//
// Undoes the effect of trackActor()
untrackActor: function(actor) {
this._untrackActor(actor);
},
// removeActor:
// @actor: a child of the chrome layer
//
// Removes @actor from the chrome layer
removeActor: function(actor) {
this.actor.remove_actor(actor);
this._untrackActor(actor);
},
_findActor: function(actor) {
for (let i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i];
if (actorData.actor == actor)
return i;
}
return -1;
},
_trackActor: function(actor, params) {
if (this._findActor(actor) != -1)
throw new Error('trying to re-track existing chrome actor');
let actorData = Params.parse(params, defaultParams);
actorData.actor = actor;
actorData.visibleId = actor.connect('notify::visible',
Lang.bind(this, this._queueUpdateRegions));
actorData.allocationId = actor.connect('notify::allocation',
Lang.bind(this, this._queueUpdateRegions));
actorData.parentSetId = actor.connect('parent-set',
Lang.bind(this, this._actorReparented));
// Note that destroying actor will unset its parent, so we don't
// need to connect to 'destroy' too.
this._trackedActors.push(actorData);
this._queueUpdateRegions();
},
_untrackActor: function(actor) {
let i = this._findActor(actor);
if (i == -1)
return;
let actorData = this._trackedActors[i];
this._trackedActors.splice(i, 1);
actor.disconnect(actorData.visibleId);
actor.disconnect(actorData.allocationId);
actor.disconnect(actorData.parentSetId);
this._queueUpdateRegions();
},
_actorReparented: function(actor, oldParent) {
if (!this.actor.contains(actor))
this._untrackActor(actor);
},
_updateVisibility: function() {
for (let i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i];
if (!this._inOverview && !actorData.visibleInFullscreen &&
this._findMonitorForActor(actorData.actor).inFullscreen)
this.actor.set_skip_paint(actorData.actor, true);
else
this.actor.set_skip_paint(actorData.actor, false);
}
},
_overviewShowing: function() {
this._inOverview = true;
this._updateVisibility();
this._queueUpdateRegions();
},
_overviewHidden: function() {
this._inOverview = false;
this._updateVisibility();
this._queueUpdateRegions();
},
_relayout: function() {
this._monitors = Main.layoutManager.monitors;
this._primaryMonitor = Main.layoutManager.primaryMonitor;
this._updateFullscreen();
this._updateVisibility();
this._queueUpdateRegions();
},
_onScreenSaverActiveChanged: function(proxy, screenSaverActive) {
this.actor.visible = !screenSaverActive;
this._queueUpdateRegions();
},
_findMonitorForRect: function(x, y, w, h) {
// First look at what monitor the center of the rectangle is at
let cx = x + w/2;
let cy = y + h/2;
for (let i = 0; i < this._monitors.length; i++) {
let monitor = this._monitors[i];
if (cx >= monitor.x && cx < monitor.x + monitor.width &&
cy >= monitor.y && cy < monitor.y + monitor.height)
return monitor;
}
// If the center is not on a monitor, return the first overlapping monitor
for (let i = 0; i < this._monitors.length; i++) {
let monitor = this._monitors[i];
if (x + w > monitor.x && x < monitor.x + monitor.width &&
y + h > monitor.y && y < monitor.y + monitor.height)
return monitor;
}
// otherwise on no monitor
return null;
},
_findMonitorForWindow: function(window) {
return this._findMonitorForRect(window.x, window.y, window.width, window.height);
},
// This call guarantees that we return some monitor to simplify usage of it
// In practice all tracked actors should be visible on some monitor anyway
_findMonitorForActor: function(actor) {
let [x, y] = actor.get_transformed_position();
let [w, h] = actor.get_transformed_size();
let monitor = this._findMonitorForRect(x, y, w, h);
if (monitor)
return monitor;
return this._primaryMonitor; // Not on any monitor, pretend its on the primary
},
_queueUpdateRegions: function() {
if (!this._updateRegionIdle)
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions),
Meta.PRIORITY_BEFORE_REDRAW);
},
_updateFullscreen: function() {
let windows = Main.getWindowActorsForWorkspace(global.screen.get_active_workspace_index());
// Reset all monitors to not fullscreen
for (let i = 0; i < this._monitors.length; i++)
this._monitors[i].inFullscreen = false;
// The chrome layer should be visible unless there is a window
// with layer FULLSCREEN, or a window with layer
// OVERRIDE_REDIRECT that covers the whole screen.
// ('override_redirect' is not actually a layer above all
// other windows, but this seems to be how mutter treats it
// currently...) If we wanted to be extra clever, we could
// figure out when an OVERRIDE_REDIRECT window was trying to
// partially overlap us, and then adjust the input region and
// our clip region accordingly...
// @windows is sorted bottom to top.
for (let i = windows.length - 1; i > -1; i--) {
let window = windows[i];
let layer = window.get_meta_window().get_layer();
if (layer == Meta.StackLayer.FULLSCREEN) {
let monitor = this._findMonitorForWindow(window);
if (monitor)
monitor.inFullscreen = true;
}
if (layer == Meta.StackLayer.OVERRIDE_REDIRECT) {
let monitor = this._findMonitorForWindow(window);
if (monitor &&
window.x <= monitor.x &&
window.x + window.width >= monitor.x + monitor.width &&
window.y <= monitor.y &&
window.y + window.height >= monitor.y + monitor.height)
monitor.inFullscreen = true;
} else
break;
}
},
_windowsRestacked: function() {
let wasInFullscreen = [];
for (let i = 0; i < this._monitors.length; i++)
wasInFullscreen[i] = this._monitors[i].inFullscreen;
this._updateFullscreen();
let changed = false;
for (let i = 0; i < wasInFullscreen.length; i++) {
if (wasInFullscreen[i] != this._monitors[i].inFullscreen) {
changed = true;
break;
}
}
if (changed) {
this._updateVisibility();
this._queueUpdateRegions();
}
},
_updateRegions: function() {
let rects = [], struts = [], i;
delete this._updateRegionIdle;
for (i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i];
if (!actorData.affectsInputRegion && !actorData.affectsStruts)
continue;
let [x, y] = actorData.actor.get_transformed_position();
let [w, h] = actorData.actor.get_transformed_size();
x = Math.round(x);
y = Math.round(y);
w = Math.round(w);
h = Math.round(h);
let rect = new Meta.Rectangle({ x: x, y: y, width: w, height: h});
if (actorData.affectsInputRegion &&
actorData.actor.get_paint_visibility() &&
!this.actor.get_skip_paint(actorData.actor))
rects.push(rect);
if (!actorData.affectsStruts)
continue;
// Limit struts to the size of the screen
let x1 = Math.max(x, 0);
let x2 = Math.min(x + w, global.screen_width);
let y1 = Math.max(y, 0);
let y2 = Math.min(y + h, global.screen_height);
// NetWM struts are not really powerful enought to handle
// a multi-monitor scenario, they only describe what happens
// around the outer sides of the full display region. However
// it can describe a partial region along each side, so
// we can support having the struts only affect the
// primary monitor. This should be enough as we only have
// chrome affecting the struts on the primary monitor so
// far.
//
// Metacity wants to know what side of the screen the
// strut is considered to be attached to. If the actor is
// only touching one edge, or is touching the entire
// border of the primary monitor, then it's obvious which
// side to call it. If it's in a corner, we pick a side
// arbitrarily. If it doesn't touch any edges, or it spans
// the width/height across the middle of the screen, then
// we don't create a strut for it at all.
let side;
let primary = this._primaryMonitor;
if (x1 <= primary.x && x2 >= primary.x + primary.width) {
if (y1 <= primary.y)
side = Meta.Side.TOP;
else if (y2 >= primary.y + primary.height)
side = Meta.Side.BOTTOM;
else
continue;
} else if (y1 <= primary.y && y2 >= primary.y + primary.height) {
if (x1 <= 0)
side = Meta.Side.LEFT;
else if (x2 >= global.screen_width)
side = Meta.Side.RIGHT;
else
continue;
} else if (x1 <= 0)
side = Meta.Side.LEFT;
else if (y1 <= 0)
side = Meta.Side.TOP;
else if (x2 >= global.screen_width)
side = Meta.Side.RIGHT;
else if (y2 >= global.screen_height)
side = Meta.Side.BOTTOM;
else
continue;
// Ensure that the strut rects goes all the way to the screen edge,
// as this really what mutter expects.
switch (side) {
case Meta.Side.TOP:
y1 = 0;
break;
case Meta.Side.BOTTOM:
y2 = global.screen_height;
break;
case Meta.Side.LEFT:
x1 = 0;
break;
case Meta.Side.RIGHT:
x2 = global.screen_width;
break;
}
let strutRect = new Meta.Rectangle({ x: x1, y: y1, width: x2 - x1, height: y2 - y1});
let strut = new Meta.Strut({ rect: strutRect, side: side });
struts.push(strut);
}
global.set_stage_input_region(rects);
let screen = global.screen;
for (let w = 0; w < screen.n_workspaces; w++) {
let workspace = screen.get_workspace_by_index(w);
workspace.set_builtin_struts(struts);
}
return false;
}
};
Signals.addSignalMethods(Chrome.prototype);

187
js/ui/contactDisplay.js Normal file
View File

@ -0,0 +1,187 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Folks = imports.gi.Folks
const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Util = imports.misc.util;
const IconGrid = imports.ui.iconGrid;
const Search = imports.ui.search;
const SearchDisplay = imports.ui.searchDisplay;
const MAX_SEARCH_RESULTS_ROWS = 1;
const ICON_SIZE = 81;
function launchContact(id) {
Util.spawn(['gnome-contacts', '-i', id]);
}
/* This class represents a shown contact search result in the overview */
const Contact = new Lang.Class({
Name: 'Contact',
_init: function(id) {
this._contactSys = Shell.ContactSystem.get_default();
this.individual = this._contactSys.get_individual(id);
this.actor = new St.Bin({ style_class: 'contact',
reactive: true,
track_hover: true });
let content = new St.BoxLayout( { style_class: 'contact-content',
vertical: false });
this.actor.set_child(content);
let icon = new St.Icon({ icon_type: St.IconType.FULLCOLOR,
icon_size: ICON_SIZE,
style_class: 'contact-icon' });
if (this.individual.avatar != null)
icon.gicon = this.individual.avatar;
else
icon.icon_name = 'avatar-default';
content.add(icon, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
let details = new St.BoxLayout({ style_class: 'contact-details',
vertical: true });
content.add(details, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
let email = this._contactSys.get_email_for_display(this.individual);
let aliasText = this.individual.alias ||
this.individual.full_name ||
this.individual.nickname ||
email ||
_("Unknown");
let aliasLabel = new St.Label({ text: aliasText,
style_class: 'contact-details-alias' });
details.add(aliasLabel, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
let presence = this._createPresence(this.individual.presence_type);
details.add(presence, { x_fill: false,
y_fill: true,
x_align: St.Align.START,
y_align: St.Align.END });
},
_createPresence: function(presence) {
let text;
let iconName;
switch(presence) {
case Folks.PresenceType.AVAILABLE:
text = _("Available");
iconName = 'user-available';
break;
case Folks.PresenceType.AWAY:
case Folks.PresenceType.EXTENDED_AWAY:
text = _("Away");
iconName = 'user-away';
break;
case Folks.PresenceType.BUSY:
text = _("Busy");
iconName = 'user-busy';
break;
case Folks.PresenceType.OFFLINE:
text = _("Offline");
iconName = 'user-offline';
break;
default:
text = '';
iconName = null;
}
let box = new St.BoxLayout({ vertical: false,
style_class: 'contact-details-status' });
if (iconName) {
let icon = new St.Icon({ icon_name: iconName,
icon_type: St.IconType.FULLCOLOR,
icon_size: 16,
style_class: 'contact-details-status-icon' });
box.add(icon, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
}
let label = new St.Label({ text: text });
box.add(label, { x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.START });
return box;
},
createIcon: function(size) {
let tc = St.TextureCache.get_default();
let icon = this.individual.avatar;
if (icon != null) {
return tc.load_gicon(null, icon, size);
} else {
return tc.load_icon_name(null, 'avatar-default', St.IconType.FULLCOLOR, size);
}
},
});
/* Searches for and returns contacts */
const ContactSearchProvider = new Lang.Class({
Name: 'ContactSearchProvider',
Extends: Search.SearchProvider,
_init: function() {
this.parent(_("CONTACTS"));
this._contactSys = Shell.ContactSystem.get_default();
},
getResultMeta: function(id) {
let contact = new Contact(id);
return { 'id': id,
'name': contact.alias,
'createIcon': function(size) {
return contact.createIcon(size);
}
};
},
getInitialResultSet: function(terms) {
return this._contactSys.initial_search(terms);
},
getSubsearchResultSet: function(previousResults, terms) {
return this._contactSys.subsearch(previousResults, terms);
},
createResultActor: function(resultMeta, terms) {
let contact = new Contact(resultMeta.id);
return contact.actor;
},
createResultContainerActor: function() {
let grid = new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START });
grid.actor.style_class = 'contact-grid';
let actor = new SearchDisplay.GridSearchResults(this, grid);
return actor;
},
activateResult: function(id, params) {
launchContact(id);
}
});

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gdk = imports.gi.Gdk;
@ -22,11 +22,9 @@ const SortGroup = {
BOTTOM: 2
};
function CtrlAltTabManager() {
this._init();
}
const CtrlAltTabManager = new Lang.Class({
Name: 'CtrlAltTabManager',
CtrlAltTabManager.prototype = {
_init: function() {
this._items = [];
this._focusManager = St.FocusManager.get_for_stage(global.stage);
@ -94,7 +92,7 @@ CtrlAltTabManager.prototype = {
return a.x - b.x;
},
popup: function(backwards) {
popup: function(backwards, mask) {
// Start with the set of focus groups that are currently mapped
let items = this._items.filter(function (item) { return item.proxy.mapped; });
@ -123,19 +121,26 @@ CtrlAltTabManager.prototype = {
return;
items.sort(Lang.bind(this, this._sortItems));
new CtrlAltTabPopup().show(items, backwards);
if (!this._popup) {
this._popup = new CtrlAltTabPopup();
this._popup.show(items, backwards, mask);
this._popup.actor.connect('destroy',
Lang.bind(this, function() {
this._popup = null;
}));
}
}
};
});
function mod(a, b) {
return (a + b) % b;
}
function CtrlAltTabPopup() {
this._init();
}
const CtrlAltTabPopup = new Lang.Class({
Name: 'CtrlAltTabPopup',
CtrlAltTabPopup.prototype = {
_init : function() {
this.actor = new Shell.GenericContainer({ name: 'ctrlAltTabPopup',
reactive: true });
@ -147,6 +152,7 @@ CtrlAltTabPopup.prototype = {
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._haveModal = false;
this._modifierMask = 0;
this._selection = 0;
Main.uiGroup.add_actor(this.actor);
@ -177,16 +183,17 @@ CtrlAltTabPopup.prototype = {
let [childMinHeight, childNaturalHeight] = this._switcher.actor.get_preferred_height(primary.width - hPadding);
let [childMinWidth, childNaturalWidth] = this._switcher.actor.get_preferred_width(childNaturalHeight);
childBox.x1 = Math.max(primary.x + leftPadding, primary.x + Math.floor((primary.width - childNaturalWidth) / 2));
childBox.x2 = Math.min(primary.width - hPadding, childBox.x1 + childNaturalWidth);
childBox.x2 = Math.min(primary.x + primary.width - hPadding, childBox.x1 + childNaturalWidth);
childBox.y1 = primary.y + Math.floor((primary.height - childNaturalHeight) / 2);
childBox.y2 = childBox.y1 + childNaturalHeight;
this._switcher.actor.allocate(childBox, flags);
},
show : function(items, startBackwards) {
show : function(items, startBackwards, mask) {
if (!Main.pushModal(this.actor))
return false;
this._haveModal = true;
this._modifierMask = AltTab.primaryModifier(mask);
this._keyPressEventId = this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
this._keyReleaseEventId = this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
@ -200,7 +207,7 @@ CtrlAltTabPopup.prototype = {
this._select(this._selection);
let [x, y, mods] = global.get_pointer();
if (!(mods & Gdk.ModifierType.MOD1_MASK)) {
if (!(mods & this._modifierMask)) {
this._finish();
return false;
}
@ -246,7 +253,7 @@ CtrlAltTabPopup.prototype = {
_keyReleaseEvent : function(actor, event) {
let [x, y, mods] = global.get_pointer();
let state = mods & Clutter.ModifierType.MOD1_MASK;
let state = mods & this._modifierMask;
if (state == 0)
this._finish();
@ -292,17 +299,14 @@ CtrlAltTabPopup.prototype = {
this._selection = num;
this._switcher.highlight(num);
}
};
});
function CtrlAltTabSwitcher(items) {
this._init(items);
}
CtrlAltTabSwitcher.prototype = {
__proto__ : AltTab.SwitcherList.prototype,
const CtrlAltTabSwitcher = new Lang.Class({
Name: 'CtrlAltTabSwitcher',
Extends: AltTab.SwitcherList,
_init : function(items) {
AltTab.SwitcherList.prototype._init.call(this, true);
this.parent(true);
for (let i = 0; i < items.length; i++)
this._addIcon(items[i]);
@ -325,4 +329,4 @@ CtrlAltTabSwitcher.prototype = {
this.addItem(box, text);
}
};
});

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Signals = imports.signals;
@ -6,6 +6,7 @@ const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Mainloop = imports.mainloop;
const AppDisplay = imports.ui.appDisplay;
const AppFavorites = imports.ui.appFavorites;
@ -16,14 +17,15 @@ const Tweener = imports.ui.tweener;
const Workspace = imports.ui.workspace;
const DASH_ANIMATION_TIME = 0.2;
const DASH_ITEM_LABEL_SHOW_TIME = 0.15;
const DASH_ITEM_LABEL_HIDE_TIME = 0.1;
const DASH_ITEM_HOVER_TIMEOUT = 300;
// A container like StBin, but taking the child's scale into account
// when requesting a size
function DashItemContainer() {
this._init();
}
const DashItemContainer = new Lang.Class({
Name: 'DashItemContainer',
DashItemContainer.prototype = {
_init: function() {
this.actor = new Shell.GenericContainer({ style_class: 'dash-item-container' });
this.actor.connect('get-preferred-width',
@ -34,9 +36,12 @@ DashItemContainer.prototype = {
Lang.bind(this, this._allocate));
this.actor._delegate = this;
this._label = null;
this.child = null;
this._childScale = 1;
this._childOpacity = 255;
this.animatingOut = false;
},
_allocate: function(actor, box, flags) {
@ -85,6 +90,60 @@ DashItemContainer.prototype = {
alloc.natural_size = natWidth * this.child.scale_y;
},
showLabel: function() {
if (this._label == null)
return;
this._label.opacity = 0;
this._label.show();
let [stageX, stageY] = this.actor.get_transformed_position();
let itemHeight = this.actor.allocation.y2 - this.actor.allocation.y1;
let labelHeight = this._label.get_height();
let yOffset = Math.floor((itemHeight - labelHeight) / 2)
let y = stageY + yOffset;
let node = this._label.get_theme_node();
let xOffset = node.get_length('-x-offset');
let x;
if (St.Widget.get_default_direction () == St.TextDirection.RTL)
x = stageX - this._label.get_width() - xOffset;
else
x = stageX + this.actor.get_width() + xOffset;
this._label.set_position(x, y);
Tweener.addTween(this._label,
{ opacity: 255,
time: DASH_ITEM_LABEL_SHOW_TIME,
transition: 'easeOutQuad',
});
},
setLabelText: function(text) {
if (this._label == null)
this._label = new St.Label({ style_class: 'dash-label'});
this._label.set_text(text);
Main.layoutManager.addChrome(this._label);
this._label.hide();
},
hideLabel: function () {
this._label.opacity = 255;
Tweener.addTween(this._label,
{ opacity: 0,
time: DASH_ITEM_LABEL_HIDE_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this._label.hide();
})
});
},
setChild: function(actor) {
if (this.child == actor)
return;
@ -115,6 +174,7 @@ DashItemContainer.prototype = {
return;
}
this.animatingOut = true;
this.childScale = 1.0;
Tweener.addTween(this,
{ childScale: 0.0,
@ -155,17 +215,14 @@ DashItemContainer.prototype = {
get childOpacity() {
return this._childOpacity;
}
};
});
function RemoveFavoriteIcon() {
this._init();
}
RemoveFavoriteIcon.prototype = {
__proto__: DashItemContainer.prototype,
const RemoveFavoriteIcon = new Lang.Class({
Name: 'RemoveFavoriteIcon',
Extends: DashItemContainer,
_init: function() {
DashItemContainer.prototype._init.call(this);
this.parent();
this._iconBin = new St.Bin({ style_class: 'remove-favorite' });
this._iconActor = null;
@ -177,12 +234,6 @@ RemoveFavoriteIcon.prototype = {
this._iconBin._delegate = this;
this.setChild(this._iconBin);
this.hiding = false;
},
animateOutAndDestroy: function() {
DashItemContainer.prototype.animateOutAndDestroy.call(this);
this.hiding = true;
},
_createIcon: function(size) {
@ -207,7 +258,7 @@ RemoveFavoriteIcon.prototype = {
let app = null;
if (source instanceof AppDisplay.AppWellIcon) {
let appSystem = Shell.AppSystem.get_default();
app = appSystem.get_app(source.getId());
app = appSystem.lookup_app(source.getId());
} else if (source.metaWindow) {
let tracker = Shell.WindowTracker.get_default();
app = tracker.get_window_app(source.metaWindow);
@ -223,28 +274,21 @@ RemoveFavoriteIcon.prototype = {
return true;
}
};
});
function DragPlaceholderItem() {
this._init();
}
DragPlaceholderItem.prototype = {
__proto__: DashItemContainer.prototype,
const DragPlaceholderItem = new Lang.Class({
Name: 'DragPlaceholderItem',
Extends: DashItemContainer,
_init: function() {
DashItemContainer.prototype._init.call(this);
this.setChild(new St.Bin({ style_class: 'dash-placeholder' }));
this.parent();
this.setChild(new St.Bin({ style_class: 'placeholder' }));
}
};
});
const Dash = new Lang.Class({
Name: 'Dash',
function Dash() {
this._init();
}
Dash.prototype = {
_init : function() {
this._maxHeight = -1;
this.iconSize = 64;
@ -254,6 +298,9 @@ Dash.prototype = {
this._dragPlaceholderPos = -1;
this._animatingPlaceholdersCount = 0;
this._favRemoveTarget = null;
this._showLabelTimeoutId = 0;
this._resetHoverTimeoutId = 0;
this._labelShowing = false;
this._box = new St.BoxLayout({ name: 'dash',
vertical: true,
@ -275,7 +322,7 @@ Dash.prototype = {
this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay));
this._tracker.connect('app-state-changed', Lang.bind(this, this._queueRedisplay));
this._appSystem.connect('app-state-changed', Lang.bind(this, this._queueRedisplay));
Main.overview.connect('item-drag-begin',
Lang.bind(this, this._onDragBegin));
@ -314,15 +361,12 @@ Dash.prototype = {
_endDrag: function() {
this._clearDragPlaceholder();
if (this._favRemoveTarget) {
this._favRemoveTarget.actor.hide();
this._adjustIconSize();
this._favRemoveTarget.actor.show();
this._favRemoveTarget.animateOutAndDestroy();
this._favRemoveTarget.actor.connect('destroy', Lang.bind(this,
function() {
this._favRemoveTarget = null;
}));
this._adjustIconSize();
}
DND.removeDragMonitor(this._dragMonitor);
},
@ -330,7 +374,7 @@ Dash.prototype = {
_onDragMotion: function(dragEvent) {
let app = null;
if (dragEvent.source instanceof AppDisplay.AppWellIcon)
app = this._appSystem.get_app(dragEvent.source.getId());
app = this._appSystem.lookup_app(dragEvent.source.getId());
else if (dragEvent.source.metaWindow)
app = this._tracker.get_window_app(dragEvent.source.metaWindow);
else
@ -390,19 +434,63 @@ Dash.prototype = {
Lang.bind(this, function() {
display.actor.opacity = 255;
}));
display.actor.set_tooltip_text(app.get_name());
let item = new DashItemContainer();
item.setChild(display.actor);
display.icon.setIconSize(this.iconSize);
item.setLabelText(app.get_name());
display.icon.setIconSize(this.iconSize);
display.actor.connect('notify::hover',
Lang.bind(this, function() {
this._onHover(item, display)
}));
return item;
},
_onHover: function (item, display) {
if (display.actor.get_hover() && !display.isMenuUp) {
if (this._showLabelTimeoutId == 0) {
let timeout = this._labelShowing ? 0 : DASH_ITEM_HOVER_TIMEOUT;
this._showLabelTimeoutId = Mainloop.timeout_add(timeout,
Lang.bind(this, function() {
this._labelShowing = true;
item.showLabel();
return false;
}));
if (this._resetHoverTimeoutId > 0) {
Mainloop.source_remove(this._resetHoverTimeoutId);
this._resetHoverTimeoutId = 0;
}
}
} else {
if (this._showLabelTimeoutId > 0)
Mainloop.source_remove(this._showLabelTimeoutId);
this._showLabelTimeoutId = 0;
item.hideLabel();
if (this._labelShowing) {
this._resetHoverTimeoutId = Mainloop.timeout_add(DASH_ITEM_HOVER_TIMEOUT,
Lang.bind(this, function() {
this._labelShowing = false;
return false;
}));
}
}
},
_adjustIconSize: function() {
let children = this._box.get_children();
if (children.length == 0) {
// For the icon size, we only consider children which are "proper"
// icons (i.e. ignoring drag placeholders) and which are not
// animating out (which means they will be destroyed at the end of
// the animation)
let iconChildren = this._box.get_children().filter(function(actor) {
return actor._delegate.child &&
actor._delegate.child._delegate &&
actor._delegate.child._delegate.icon &&
!actor._delegate.animatingOut;
});
if (iconChildren.length == 0) {
this._box.add_style_pseudo_class('empty');
return;
}
@ -412,23 +500,45 @@ Dash.prototype = {
if (this._maxHeight == -1)
return;
let iconChildren = children.filter(function(actor) {
return actor.visible &&
actor._delegate.child &&
actor._delegate.child._delegate &&
actor._delegate.child._delegate.icon;
});
// Compute the amount of extra space (or missing space) we have
// per icon with the current icon size
let [minHeight, natHeight] = this.actor.get_preferred_height(-1);
let diff = (this._maxHeight - natHeight) / iconChildren.length;
let themeNode = this._box.get_theme_node();
let maxAllocation = new Clutter.ActorBox({ x1: 0, y1: 0,
x2: 42 /* whatever */,
y2: this._maxHeight });
let maxContent = themeNode.get_content_box(maxAllocation);
let availHeight = maxContent.y2 - maxContent.y1;
let spacing = themeNode.get_length('spacing');
let firstIcon = iconChildren[0]._delegate.child._delegate.icon;
let minHeight, natHeight;
// Enforce the current icon size during the size request if
// the icon is animating
if (firstIcon._animating) {
let [currentWidth, currentHeight] = firstIcon.icon.get_size();
firstIcon.icon.set_size(this.iconSize, this.iconSize);
[minHeight, natHeight] = iconChildren[0].get_preferred_height(-1);
firstIcon.icon.set_size(currentWidth, currentHeight);
} else {
[minHeight, natHeight] = iconChildren[0].get_preferred_height(-1);
}
// Subtract icon padding and box spacing from the available height
availHeight -= iconChildren.length * (natHeight - this.iconSize) +
(iconChildren.length - 1) * spacing;
let availSize = availHeight / iconChildren.length;
let iconSizes = [ 16, 22, 24, 32, 48, 64 ];
let newIconSize = 16;
for (let i = 0; i < iconSizes.length; i++) {
if (iconSizes[i] < this.iconSize + diff)
if (iconSizes[i] < availSize)
newIconSize = iconSizes[i];
}
@ -437,6 +547,7 @@ Dash.prototype = {
let oldIconSize = this.iconSize;
this.iconSize = newIconSize;
this.emit('icon-size-changed');
let scale = oldIconSize / newIconSize;
for (let i = 0; i < iconChildren.length; i++) {
@ -458,11 +569,15 @@ Dash.prototype = {
icon.icon.set_size(icon.icon.width * scale,
icon.icon.height * scale);
icon._animating = true;
Tweener.addTween(icon.icon,
{ width: targetWidth,
height: targetHeight,
time: DASH_ANIMATION_TIME,
transition: 'easeOutQuad'
transition: 'easeOutQuad',
onComplete: function() {
icon._animating = false;
}
});
}
},
@ -470,10 +585,7 @@ Dash.prototype = {
_redisplay: function () {
let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
/* hardcode here pending some design about how exactly desktop contexts behave */
let contextId = '';
let running = this._tracker.get_running_apps(contextId);
let running = this._appSystem.get_running();
let children = this._box.get_children().filter(function(actor) {
return actor._delegate.child &&
@ -568,29 +680,7 @@ Dash.prototype = {
this._box.insert_actor(addedItems[i].item.actor,
addedItems[i].pos);
// Hide removed actors to not take them into account
// when adjusting the icon size ...
for (let i = 0; i < removedActors.length; i++)
removedActors[i].hide();
// ... and do the same for the remove target if necessary
if (this._favRemoveTarget && this._favRemoveTarget.hiding)
this._favRemoveTarget.actor.hide();
this._adjustIconSize();
if (this._favRemoveTarget && this._favRemoveTarget.hiding)
this._favRemoveTarget.actor.show();
// Skip animations on first run when adding the initial set
// of items, to avoid all items zooming in at once
if (!this._shownInitially) {
this._shownInitially = true;
return;
}
for (let i = 0; i < removedActors.length; i++) {
removedActors[i].show();
let item = removedActors[i]._delegate;
// Don't animate item removal when the overview is hidden
@ -600,6 +690,15 @@ Dash.prototype = {
item.actor.destroy();
}
this._adjustIconSize();
// Skip animations on first run when adding the initial set
// of items, to avoid all items zooming in at once
if (!this._shownInitially) {
this._shownInitially = true;
return;
}
// Don't animate item addition when the overview is hidden
if (!Main.overview.visible)
return;
@ -619,12 +718,12 @@ Dash.prototype = {
handleDragOver : function(source, actor, x, y, time) {
let app = null;
if (source instanceof AppDisplay.AppWellIcon)
app = this._appSystem.get_app(source.getId());
app = this._appSystem.lookup_app(source.getId());
else if (source.metaWindow)
app = this._tracker.get_window_app(source.metaWindow);
// Don't allow favoriting of transient apps
if (app == null || app.is_transient())
if (app == null || app.is_window_backed())
return DND.DragMotionResult.NO_DROP;
let favorites = AppFavorites.getAppFavorites().getFavorites();
@ -686,6 +785,8 @@ Dash.prototype = {
}
this._dragPlaceholder = new DragPlaceholderItem();
this._dragPlaceholder.child.set_width (this.iconSize);
this._dragPlaceholder.child.set_height (this.iconSize / 2);
this._box.insert_actor(this._dragPlaceholder.actor,
this._dragPlaceholderPos);
if (fadeIn)
@ -704,13 +805,13 @@ Dash.prototype = {
acceptDrop : function(source, actor, x, y, time) {
let app = null;
if (source instanceof AppDisplay.AppWellIcon) {
app = this._appSystem.get_app(source.getId());
app = this._appSystem.lookup_app(source.getId());
} else if (source.metaWindow) {
app = this._tracker.get_window_app(source.metaWindow);
}
// Don't allow favoriting of transient apps
if (app == null || app.is_transient()) {
if (app == null || app.is_window_backed()) {
return false;
}
@ -746,6 +847,6 @@ Dash.prototype = {
return true;
}
};
});
Signals.addSignalMethods(Dash.prototype);

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
@ -9,11 +9,13 @@ const Clutter = imports.gi.Clutter;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Params = imports.misc.params;
const Util = imports.misc.util;
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';
@ -38,29 +40,26 @@ function _onVertSepRepaint (area)
cr.stroke();
};
function DateMenuButton() {
this._init();
}
const DateMenuButton = new Lang.Class({
Name: 'DateMenuButton',
Extends: PanelMenu.Button,
DateMenuButton.prototype = {
__proto__: PanelMenu.Button.prototype,
_init: function(params) {
params = Params.parse(params, { showEvents: true });
_init: function() {
let item;
let hbox;
let vbox;
this._eventSource = new Calendar.DBusEventSource();
let menuAlignment = 0.25;
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
menuAlignment = 1.0 - menuAlignment;
PanelMenu.Button.prototype._init.call(this, menuAlignment);
this.parent(menuAlignment);
this._clock = new St.Label();
this.actor.set_child(this._clock);
this.actor.add_actor(this._clock);
hbox = new St.BoxLayout({name: 'calendarArea'});
hbox = new St.BoxLayout({name: 'calendarArea' });
this.menu.addActor(hbox);
// Fill up the first column
@ -70,46 +69,62 @@ DateMenuButton.prototype = {
// Date
this._date = new St.Label();
this.actor.label_actor = this._date;
this._date.style_class = 'datemenu-date-label';
vbox.add(this._date);
this._eventList = new Calendar.EventsList(this._eventSource);
if (params.showEvents) {
this._eventSource = new Calendar.DBusEventSource();
this._eventList = new Calendar.EventsList(this._eventSource);
} else {
this._eventSource = null;
this._eventList = null;
}
// Calendar
this._calendar = new Calendar.Calendar(this._eventSource);
this._calendar.connect('selected-date-changed',
Lang.bind(this, function(calendar, date) {
// we know this._eventList is defined here, because selected-data-changed
// only gets emitted when the user clicks a date in the calendar,
// and the calender makes those dates unclickable when instantiated with
// a null event source
this._eventList.setDate(date);
}));
vbox.add(this._calendar.actor);
item = new PopupMenu.PopupSeparatorMenuItem();
item.setColumnWidths(1);
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
item = new PopupMenu.PopupMenuItem(_("Date and Time Settings"));
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
item.actor.can_focus = false;
vbox.add(item.actor);
item = this.menu.addSettingsAction(_("Date and Time Settings"), 'gnome-datetime-panel.desktop');
if (item) {
let separator = new PopupMenu.PopupSeparatorMenuItem();
separator.setColumnWidths(1);
vbox.add(separator.actor, {y_align: St.Align.END, expand: true, y_fill: false});
// Add vertical separator
item.actor.can_focus = false;
item.actor.reparent(vbox);
}
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
pseudo_class: 'highlighted' });
item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
hbox.add(item);
if (params.showEvents) {
// Add vertical separator
// Fill up the second column
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
pseudo_class: 'highlighted' });
item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
hbox.add(item);
vbox = new St.BoxLayout({vertical: true});
hbox.add(vbox, { expand: true });
// Fill up the second column
vbox = new St.BoxLayout({name: 'calendarEventsArea',
vertical: true});
hbox.add(vbox, { expand: true });
// Event list
vbox.add(this._eventList.actor, { expand: true });
// Event list
vbox.add(this._eventList.actor, { expand: true });
item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
item.actor.can_focus = false;
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
item.actor.can_focus = false;
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
}
// Whenever the menu is opened, select today
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
@ -142,6 +157,10 @@ DateMenuButton.prototype = {
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._updateClockAndDate();
},
@ -157,12 +176,12 @@ DateMenuButton.prototype = {
switch (format) {
case '24h':
if (showDate)
/* Translators: This is the time format with date used
/* 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
/* Translators: This is the time format without date used
in 24-hour mode. */
clockFormat = showSeconds ? _("%a %R:%S")
: _("%a %R");
@ -170,12 +189,12 @@ DateMenuButton.prototype = {
case '12h':
default:
if (showDate)
/* Translators: This is a time format with date used
/* 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
/* Translators: This is a time format without date used
for AM/PM. */
clockFormat = showSeconds ? _("%a %l:%M:%S %p")
: _("%a %l:%M %p");
@ -196,16 +215,26 @@ DateMenuButton.prototype = {
return false;
},
_onPreferencesActivate: function() {
this.menu.close();
Main.overview.hide();
let app = Shell.AppSystem.get_default().get_app('gnome-datetime-panel.desktop');
app.activate(-1);
},
_onOpenCalendarActivate: function() {
this.menu.close();
// TODO: pass the selected day
Util.spawn(['evolution', '-c', 'calendar']);
let calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' });
let tool = calendarSettings.get_string('exec');
if (tool.length == 0 || tool == 'evolution') {
// TODO: pass the selected day
Util.spawn(['evolution', '-c', 'calendar']);
} else {
let needTerm = calendarSettings.get_boolean('needs-term');
if (needTerm) {
let terminalSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.terminal' });
let term = terminalSettings.get_string('exec');
let arg = terminalSettings.get_string('exec-arg');
if (arg != '')
Util.spawn([term, arg, tool]);
else
Util.spawn([term, tool]);
} else {
Util.spawnCommandLine(tool)
}
}
}
};
});

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gtk = imports.gi.Gtk;
@ -69,11 +69,9 @@ function removeDragMonitor(monitor) {
}
}
function _Draggable(actor, params) {
this._init(actor, params);
}
const _Draggable = new Lang.Class({
Name: 'Draggable',
_Draggable.prototype = {
_init : function(actor, params) {
params = Params.parse(params, { manualMode: false,
restoreOnSuccess: false,
@ -596,7 +594,7 @@ _Draggable.prototype = {
this._dragActor = undefined;
currentDraggable = null;
}
};
});
Signals.addSignalMethods(_Draggable.prototype);

View File

@ -1,19 +1,16 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const DocInfo = imports.misc.docInfo;
const Lang = imports.lang;
const Params = imports.misc.params;
const Search = imports.ui.search;
function DocSearchProvider() {
this._init();
}
DocSearchProvider.prototype = {
__proto__: Search.SearchProvider.prototype,
const DocSearchProvider = new Lang.Class({
Name: 'DocSearchProvider',
Extends: Search.SearchProvider,
_init: function(name) {
Search.SearchProvider.prototype._init.call(this, _("RECENT ITEMS"));
this.parent(_("RECENT ITEMS"));
this._docManager = DocInfo.getDocManager();
},
@ -30,11 +27,11 @@ DocSearchProvider.prototype = {
},
activateResult: function(id, params) {
params = Params.parse(params, { workspace: null,
timestamp: null });
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
let docInfo = this._docManager.lookupByUri(id);
docInfo.launch(params.workspace ? params.workspace.index() : -1);
docInfo.launch(params.workspace);
},
getInitialResultSet: function(terms) {
@ -44,4 +41,4 @@ DocSearchProvider.prototype = {
getSubsearchResultSet: function(previousResults, terms) {
return this._docManager.subsearch(previousResults, terms);
}
};
});

View File

@ -1,5 +1,5 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
*
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/*
* Copyright 2010 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or modify
@ -18,19 +18,19 @@
* 02111-1307, USA.
*/
const DBus = imports.dbus;
const Lang = imports.lang;
const Signals = imports.signals;
const AccountsService = imports.gi.AccountsService;
const Clutter = imports.gi.Clutter;
const Gdm = imports.gi.Gdm;
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 GnomeSession = imports.misc.gnomeSession
const GnomeSession = imports.misc.gnomeSession;
const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
@ -43,51 +43,68 @@ const _DIALOG_ICON_SIZE = 32;
const GSM_SESSION_MANAGER_LOGOUT_FORCE = 2;
const EndSessionDialogIface = {
name: 'org.gnome.SessionManager.EndSessionDialog',
methods: [{ name: 'Open',
inSignature: 'uuuao',
outSignature: ''
}
],
signals: [{ name: 'Canceled',
inSignature: '',
}],
properties: []
};
const EndSessionDialogIface = <interface name="org.gnome.SessionManager.EndSessionDialog">
<method name="Open">
<arg type="u" direction="in" />
<arg type="u" direction="in" />
<arg type="u" direction="in" />
<arg type="ao" direction="in" />
</method>
<signal name="ConfirmedLogout" />
<signal name="ConfirmedReboot" />
<signal name="ConfirmedShutdown" />
<signal name="Canceled" />
<signal name="Closed" />
</interface>;
const logoutDialogContent = {
subjectWithUser: _("Log Out %s"),
subject: _("Log Out"),
subjectWithUser: C_("title", "Log Out %s"),
subject: C_("title", "Log Out"),
inhibitedDescription: _("Click Log Out to quit these applications and log out of the system."),
uninhibitedDescriptionWithUser: _("%s will be logged out automatically in %d seconds."),
uninhibitedDescription: _("You will be logged out automatically in %d seconds."),
uninhibitedDescriptionWithUser: function(user, seconds) {
return ngettext("%s will be logged out automatically in %d second.",
"%s will be logged out automatically in %d seconds.",
seconds).format(user, seconds);
},
uninhibitedDescription: function(seconds) {
return ngettext("You will be logged out automatically in %d second.",
"You will be logged out automatically in %d seconds.",
seconds).format(seconds);
},
endDescription: _("Logging out of the system."),
confirmButtons: [{ signal: 'ConfirmedLogout',
label: _("Log Out") }],
label: C_("button", "Log Out") }],
iconStyleClass: 'end-session-dialog-logout-icon'
};
const shutdownDialogContent = {
subject: _("Power Off"),
subject: C_("title", "Power Off"),
inhibitedDescription: _("Click Power Off to quit these applications and power off the system."),
uninhibitedDescription: _("The system will power off automatically in %d seconds."),
uninhibitedDescription: function(seconds) {
return ngettext("The system will power off automatically in %d second.",
"The system will power off automatically in %d seconds.",
seconds).format(seconds);
},
endDescription: _("Powering off the system."),
confirmButtons: [{ signal: 'ConfirmedReboot',
label: _("Restart") },
label: C_("button", "Restart") },
{ signal: 'ConfirmedShutdown',
label: _("Power Off") }],
label: C_("button", "Power Off") }],
iconName: 'system-shutdown',
iconStyleClass: 'end-session-dialog-shutdown-icon'
};
const restartDialogContent = {
subject: _("Restart"),
subject: C_("title", "Restart"),
inhibitedDescription: _("Click Restart to quit these applications and restart the system."),
uninhibitedDescription: _("The system will restart automatically in %d seconds."),
uninhibitedDescription: function(seconds) {
return ngettext("The system will restart automatically in %d second.",
"The system will restart automatically in %d seconds.",
seconds).format(seconds);
},
endDescription: _("Restarting the system."),
confirmButtons: [{ signal: 'ConfirmedReboot',
label: _("Restart") }],
label: C_("button", "Restart") }],
iconName: 'system-shutdown',
iconStyleClass: 'end-session-dialog-shutdown-icon'
};
@ -113,7 +130,7 @@ function findAppFromInhibitor(inhibitor) {
let app = null;
for (let i = 0; i < candidateDesktopFiles.length; i++) {
try {
app = appSystem.get_app(candidateDesktopFiles[i]);
app = appSystem.lookup_app(candidateDesktopFiles[i]);
if (app)
break;
@ -125,11 +142,9 @@ function findAppFromInhibitor(inhibitor) {
return app;
}
function ListItem(app, reason) {
this._init(app, reason);
}
const ListItem = new Lang.Class({
Name: 'ListItem',
ListItem.prototype = {
_init: function(app, reason) {
this._app = app;
this._reason = reason;
@ -173,9 +188,9 @@ ListItem.prototype = {
_onClicked: function() {
this.emit('activate');
this._app.activate(-1);
this._app.activate();
}
};
});
Signals.addSignalMethods(ListItem.prototype);
// The logout timer only shows updates every 10 seconds
@ -213,31 +228,21 @@ function _setLabelText(label, text) {
}
}
function EndSessionDialog() {
if (_endSessionDialog == null) {
this._init();
DBus.session.exportObject('/org/gnome/SessionManager/EndSessionDialog',
this);
_endSessionDialog = this;
}
return _endSessionDialog;
}
function init() {
// This always returns the same singleton object
// By instantiating it initially, we register the
// bus object, etc.
let dialog = new EndSessionDialog();
_endSessionDialog = new EndSessionDialog();
}
EndSessionDialog.prototype = {
__proto__: ModalDialog.ModalDialog.prototype,
const EndSessionDialog = new Lang.Class({
Name: 'EndSessionDialog',
Extends: ModalDialog.ModalDialog,
_init: function() {
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'end-session-dialog' });
this.parent({ styleClass: 'end-session-dialog' });
this._user = Gdm.UserManager.ref_default().get_user(GLib.get_user_name());
this._user = AccountsService.UserManager.get_default().get_user(GLib.get_user_name());
this._secondsLeft = 0;
this._totalSecondsToStayOpen = 0;
@ -310,6 +315,9 @@ EndSessionDialog.prototype = {
if (this._applicationList.get_children().length == 0)
scrollView.hide();
}));
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(EndSessionDialogIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/SessionManager/EndSessionDialog');
},
_onDestroy: function() {
@ -325,7 +333,8 @@ EndSessionDialog.prototype = {
this._iconBin.child = null;
if (iconFile) {
this._iconBin.show();
this._iconBin.set_style('background-image: url("' + iconFile + '");');
this._iconBin.set_style('background-image: url("' + iconFile + '");' +
'background-size: contain;');
} else {
this._iconBin.hide();
}
@ -388,14 +397,14 @@ EndSessionDialog.prototype = {
subject = dialogContent.subjectWithUser.format(realName);
if (dialogContent.uninhibitedDescriptionWithUser)
description = dialogContent.uninhibitedDescriptionWithUser.format(realName, displayTime);
description = dialogContent.uninhibitedDescriptionWithUser(realName, displayTime);
else
description = dialogContent.uninhibitedDescription.format(displayTime);
description = dialogContent.uninhibitedDescription(displayTime);
}
}
if (!description)
description = dialogContent.uninhibitedDescription.format(displayTime);
description = dialogContent.uninhibitedDescription(displayTime);
} else {
description = dialogContent.endDescription;
}
@ -423,26 +432,20 @@ EndSessionDialog.prototype = {
},
close: function() {
ModalDialog.ModalDialog.prototype.close.call(this);
DBus.session.emit_signal('/org/gnome/SessionManager/EndSessionDialog',
'org.gnome.SessionManager.EndSessionDialog',
'Closed', '', []);
this.parent();
this._dbusImpl.emit_signal('Closed', null);
},
cancel: function() {
this._stopTimer();
DBus.session.emit_signal('/org/gnome/SessionManager/EndSessionDialog',
'org.gnome.SessionManager.EndSessionDialog',
'Canceled', '', []);
this._dbusImpl.emit_signal('Canceled', null);
this.close(global.get_current_time());
},
_confirm: function(signal) {
this._fadeOutDialog();
this._stopTimer();
DBus.session.emit_signal('/org/gnome/SessionManager/EndSessionDialog',
'org.gnome.SessionManager.EndSessionDialog',
signal, '', []);
this._dbusImpl.emit_signal(signal, null);
},
_onOpened: function() {
@ -494,39 +497,41 @@ EndSessionDialog.prototype = {
this._updateContent();
},
OpenAsync: function(type, timestamp, totalSecondsToStayOpen, inhibitorObjectPaths, callback) {
OpenAsync: function(parameters, invocation) {
let [type, timestamp, totalSecondsToStayOpen, inhibitorObjectPaths] = parameters;
this._totalSecondsToStayOpen = totalSecondsToStayOpen;
this._inhibitors = [];
this._applicationList.destroy_children();
this._type = type;
if (!(this._type in DialogContent))
throw new DBus.DBusError('org.gnome.Shell.ModalDialog.TypeError',
"Unknown dialog type requested");
if (!(this._type in DialogContent)) {
invocation.report_dbus_error('org.gnome.Shell.ModalDialog.TypeError',
"Unknown dialog type requested");
return;
}
for (let i = 0; i < inhibitorObjectPaths.length; i++) {
let inhibitor = new GnomeSession.Inhibitor(inhibitorObjectPaths[i]);
let inhibitor = new GnomeSession.Inhibitor(inhibitorObjectPaths[i], Lang.bind(this, function(proxy, error) {
this._onInhibitorLoaded(proxy);
}));
inhibitor.connect('is-loaded',
Lang.bind(this, function() {
this._onInhibitorLoaded(inhibitor);
}));
this._inhibitors.push(inhibitor);
}
this._updateButtons();
if (!this.open(timestamp))
throw new DBus.DBusError('org.gnome.Shell.ModalDialog.GrabError',
"Cannot grab pointer and keyboard");
if (!this.open(timestamp)) {
invocation.report_dbus_error('org.gnome.Shell.ModalDialog.GrabError',
"Cannot grab pointer and keyboard");
return;
}
this._updateContent();
let signalId = this.connect('opened',
Lang.bind(this, function() {
callback();
invocation.return_value(null);
this.disconnect(signalId);
}));
}
};
DBus.conformExport(EndSessionDialog.prototype, EndSessionDialogIface);
});

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
imports.gi.versions.Clutter = '1.0';
imports.gi.versions.Gio = '2.0';

View File

@ -1,133 +1,287 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
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,
DISABLED: 2,
ERROR: 3,
OUT_OF_DATE: 4
OUT_OF_DATE: 4,
DOWNLOADING: 5,
INITIALIZED: 6,
// Used as an error state for operations on unknown extensions,
// should never be in a real extensionMeta object.
UNINSTALLED: 99
};
const ExtensionType = {
SYSTEM: 1,
PER_USER: 2
};
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/';
// Maps uuid -> metadata object
const extensionMeta = {};
// Maps uuid -> importer object (extension directory tree)
const extensions = {};
// Arrays of uuids
var disabledExtensions;
var enabledExtensions;
// GFile for user extensions
var userExtensionsDir = null;
const _httpSession = new Soup.SessionAsync();
/**
* versionCheck:
* @required: an array of versions we're compatible with
* @current: the version we have
*
* Check if a component is compatible for an extension.
* @required is an array, and at least one version must match.
* @current must be in the format <major>.<minor>.<point>.<micro>
* <micro> is always ignored
* <point> is ignored if <minor> is even (so you can target the
* whole stable release)
* <minor> and <major> must match
* Each target version must be at least <major> and <minor>
*/
function versionCheck(required, current) {
let currentArray = current.split('.');
let major = currentArray[0];
let minor = currentArray[1];
let point = currentArray[2];
for (let i = 0; i < required.length; i++) {
let requiredArray = required[i].split('.');
if (requiredArray[0] == major &&
requiredArray[1] == minor &&
(requiredArray[2] == point ||
(requiredArray[2] == undefined && parseInt(minor) % 2 == 0)))
return true;
}
return false;
// The unfortunate state of gjs, gobject-introspection and libsoup
// means that I have to do a hack to add a feature.
// See: https://bugzilla.gnome.org/show_bug.cgi?id=655189 for context.
if (Soup.Session.prototype.add_feature != null)
Soup.Session.prototype.add_feature.call(_httpSession, new Soup.ProxyResolverDefault());
function _getCertFile() {
let localCert = GLib.build_filenamev([global.userdatadir, 'extensions.gnome.org.crt']);
if (GLib.file_test(localCert, GLib.FileTest.EXISTS))
return localCert;
else
return Config.SHELL_SYSTEM_CA_FILE;
}
function loadExtension(dir, enabled, type) {
let info;
let baseErrorString = 'While loading extension from "' + dir.get_parse_name() + '": ';
_httpSession.ssl_ca_file = _getCertFile();
let metadataFile = dir.get_child('metadata.json');
if (!metadataFile.query_exists(null)) {
global.logError(baseErrorString + 'Missing metadata.json');
// Arrays of uuids
var enabledExtensions;
// Contains the order that extensions were enabled in.
const extensionOrder = [];
// We don't really have a class to add signals on. So, create
// a simple dummy object, add the signal methods, and export those
// publically.
var _signals = {};
Signals.addSignalMethods(_signals);
const connect = Lang.bind(_signals, _signals.connect);
const disconnect = Lang.bind(_signals, _signals.disconnect);
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
function installExtensionFromUUID(uuid, version_tag) {
let params = { uuid: uuid,
version_tag: version_tag,
shell_version: Config.PACKAGE_VERSION,
api_version: API_VERSION.toString() };
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, version_tag, info.name);
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];
delete extensionStateObjs[uuid];
delete errors[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 metadataContents;
// FIXME: use a GFile mkstemp-type method once one exists
let fd, tmpzip;
try {
metadataContents = Shell.get_file_contents_utf8_sync(metadataFile.get_path());
[fd, tmpzip] = GLib.file_open_tmp('XXXXXX.shell-extension.zip');
} catch (e) {
global.logError(baseErrorString + 'Failed to load metadata.json: ' + e);
logExtensionError(uuid, 'tempfile: ' + e.toString());
return;
}
let meta;
try {
meta = JSON.parse(metadataContents);
} catch (e) {
global.logError(baseErrorString + 'Failed to parse metadata.json: ' + e);
let stream = new Gio.UnixOutputStream({ fd: fd });
let dir = ExtensionUtils.userExtensionsDir.get_child(uuid);
Shell.write_soup_message_to_stream(stream, message);
stream.close(null);
let [success, pid] = GLib.spawn_async(null,
['unzip', '-uod', dir.get_path(), '--', tmpzip],
null,
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
null);
if (!success) {
logExtensionError(uuid, 'extract: could not extract');
return;
}
let requiredProperties = ['uuid', 'name', 'description', 'shell-version'];
for (let i = 0; i < requiredProperties.length; i++) {
let prop = requiredProperties[i];
if (!meta[prop]) {
global.logError(baseErrorString + 'missing "' + prop + '" property in metadata.json');
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)
return;
if (extension.state != ExtensionState.ENABLED)
return;
let extensionState = extensionStateObjs[uuid];
// "Rebase" the extension order by disabling and then enabling extensions
// in order to help prevent conflicts.
// Example:
// order = [A, B, C, D, E]
// user disables C
// this should: disable E, disable D, disable C, enable D, enable E
let orderIdx = extensionOrder.indexOf(uuid);
let order = extensionOrder.slice(orderIdx + 1);
let orderReversed = order.slice().reverse();
for (let i = 0; i < orderReversed.length; i++) {
let uuid = orderReversed[i];
try {
extensionStateObjs[uuid].disable();
} catch(e) {
logExtensionError(uuid, e.toString());
}
}
if (extensions[meta.uuid] != undefined) {
global.logError(baseErrorString + "extension already loaded");
try {
extensionState.disable();
} catch(e) {
logExtensionError(uuid, e.toString());
return;
}
// Encourage people to add this
if (!meta['url']) {
global.log(baseErrorString + 'Warning: Missing "url" property in metadata.json');
for (let i = 0; i < order.length; i++) {
let uuid = order[i];
try {
extensionStateObjs[uuid].enable();
} catch(e) {
logExtensionError(uuid, e.toString());
}
}
let base = dir.get_basename();
if (base != meta.uuid) {
global.logError(baseErrorString + 'uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + base + '"');
extensionOrder.splice(orderIdx, 1);
extension.state = ExtensionState.DISABLED;
_signals.emit('extension-state-changed', extension);
}
function enableExtension(uuid) {
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return;
if (extension.state == ExtensionState.INITIALIZED) {
loadExtension(extension.dir, extension.type, true);
return;
}
if (!versionCheck(meta['shell-version'], Config.PACKAGE_VERSION) ||
(meta['js-version'] && !versionCheck(meta['js-version'], Config.GJS_VERSION))) {
global.logError(baseErrorString + 'extension is not compatible with current GNOME Shell and/or GJS version');
if (extension.state != ExtensionState.DISABLED)
return;
extensionOrder.push(uuid);
try {
extension.stateObj.enable();
} catch(e) {
logExtensionError(uuid, e.toString());
return;
}
extensionMeta[meta.uuid] = meta;
extensionMeta[meta.uuid].type = type;
extensionMeta[meta.uuid].path = dir.get_path();
if (!enabled) {
extensionMeta[meta.uuid].state = ExtensionState.DISABLED;
extension.state = ExtensionState.ENABLED;
_signals.emit('extension-state-changed', extension);
}
function logExtensionError(uuid, message, state) {
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return;
if (!extension.errors)
extension.errors = [];
extension.errors.push(message);
global.logError('Extension "%s" had error: %s'.format(uuid, message));
state = state || ExtensionState.ERROR;
_signals.emit('extension-state-changed', { uuid: uuid,
error: message,
state: state });
}
function loadExtension(dir, type, enabled) {
let uuid = dir.get_basename();
let extension;
if (ExtensionUtils.extensions[uuid] != undefined) {
throw new Error('extension already loaded');
}
try {
extension = ExtensionUtils.createExtensionObject(uuid, dir, type);
} catch(e) {
logExtensionError(uuid, e.message);
return;
}
// Default to error, we set success as the last step
extensionMeta[meta.uuid].state = ExtensionState.ERROR;
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);
extension.state = ExtensionState.OUT_OF_DATE;
return;
}
if (!enabled) {
extension.state = ExtensionState.INITIALIZED;
return;
}
let extensionJs = dir.get_child('extension.js');
if (!extensionJs.query_exists(null)) {
global.logError(baseErrorString + 'Missing extension.js');
logExtensionError(uuid, 'Missing extension.js');
return;
}
let stylesheetPath = null;
@ -138,82 +292,158 @@ function loadExtension(dir, enabled, type) {
try {
theme.load_stylesheet(stylesheetFile.get_path());
} catch (e) {
global.logError(baseErrorString + 'Stylesheet parse error: ' + e);
logExtensionError(uuid, 'Stylesheet parse error: ' + e);
return;
}
}
let extensionModule;
let extensionState = null;
try {
global.add_extension_importer('imports.ui.extensionSystem.extensions', meta.uuid, dir.get_path());
extensionModule = extensions[meta.uuid].extension;
ExtensionUtils.installImporter(extension);
extensionModule = extension.imports.extension;
} catch (e) {
if (stylesheetPath != null)
theme.unload_stylesheet(stylesheetPath);
global.logError(baseErrorString + e);
logExtensionError(uuid, '' + e);
return;
}
if (!extensionModule.main) {
global.logError(baseErrorString + 'missing \'main\' function');
if (!extensionModule.init) {
logExtensionError(uuid, 'missing \'init\' function');
return;
}
try {
extensionModule.main(meta);
extensionState = extensionModule.init(extension);
} catch (e) {
if (stylesheetPath != null)
theme.unload_stylesheet(stylesheetPath);
global.logError(baseErrorString + 'Failed to evaluate main function:' + e);
logExtensionError(uuid, 'Failed to evaluate init function:' + e);
return;
}
extensionMeta[meta.uuid].state = ExtensionState.ENABLED;
global.log('Loaded extension ' + meta.uuid);
if (!extensionState)
extensionState = extensionModule;
extension.stateObj = extensionState;
if (!extensionState.enable) {
logExtensionError(uuid, 'missing \'enable\' function');
return;
}
if (!extensionState.disable) {
logExtensionError(uuid, 'missing \'disable\' function');
return;
}
extension.state = ExtensionState.DISABLED;
enableExtension(uuid);
_signals.emit('extension-loaded', uuid);
_signals.emit('extension-state-changed', extension);
global.log('Loaded extension ' + uuid);
}
function onEnabledExtensionsChanged() {
let newEnabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
// Find and enable all the newly enabled extensions: UUIDs found in the
// new setting, but not in the old one.
newEnabledExtensions.filter(function(uuid) {
return enabledExtensions.indexOf(uuid) == -1;
}).forEach(function(uuid) {
enableExtension(uuid);
});
// Find and disable all the newly disabled extensions: UUIDs found in the
// old setting, but not in the new one.
enabledExtensions.filter(function(item) {
return newEnabledExtensions.indexOf(item) == -1;
}).forEach(function(uuid) {
disableExtension(uuid);
});
enabledExtensions = newEnabledExtensions;
}
function init() {
let userExtensionsPath = GLib.build_filenamev([global.userdatadir, 'extensions']);
userExtensionsDir = Gio.file_new_for_path(userExtensionsPath);
try {
userExtensionsDir.make_directory_with_parents(null);
} catch (e) {
global.logError('' + e);
}
ExtensionUtils.init();
disabledExtensions = global.settings.get_strv('disabled-extensions', -1);
enabledExtensions = global.settings.get_strv('enabled-extensions', -1);
}
function _loadExtensionsIn(dir, type) {
let fileEnum;
let file, info;
try {
fileEnum = dir.enumerate_children('standard::*', Gio.FileQueryInfoFlags.NONE, null);
} catch (e) {
global.logError('' + e);
return;
}
while ((info = fileEnum.next_file(null)) != null) {
let fileType = info.get_file_type();
if (fileType != Gio.FileType.DIRECTORY)
continue;
let name = info.get_name();
// Enable all but disabled extensions if enabledExtensions is not set.
// If it is set, enable one those, except they are disabled as well.
let enabled = (enabledExtensions.length == 0 || enabledExtensions.indexOf(name) >= 0)
&& disabledExtensions.indexOf(name) < 0;
let child = dir.get_child(name);
loadExtension(child, enabled, type);
}
fileEnum.close(null);
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
}
function loadExtensions() {
_loadExtensionsIn(userExtensionsDir, ExtensionType.PER_USER);
let systemDataDirs = GLib.get_system_data_dirs();
for (let i = 0; i < systemDataDirs.length; i++) {
let dirPath = systemDataDirs[i] + '/gnome-shell/extensions';
let dir = Gio.file_new_for_path(dirPath);
if (dir.query_exists(null))
_loadExtensionsIn(dir, ExtensionType.SYSTEM);
}
ExtensionUtils.scanExtensions(function(uuid, dir, type) {
let enabled = enabledExtensions.indexOf(uuid) != -1;
loadExtension(dir, type, enabled);
});
}
const InstallExtensionDialog = new Lang.Class({
Name: 'InstallExtensionDialog',
Extends: ModalDialog.ModalDialog,
_init: function(uuid, version_tag, name) {
this.parent({ styleClass: 'extension-dialog' });
this._uuid = uuid;
this._version_tag = version_tag;
this._name = name;
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(name);
this._descriptionLabel = new St.Label({ text: message });
this.contentLayout.add(this._descriptionLabel,
{ y_fill: true,
y_align: St.Align.START });
},
_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 extension = { uuid: this._uuid,
state: ExtensionState.DOWNLOADING,
error: '' };
ExtensionUtils.extensions[this._uuid] = extension;
_signals.emit('extension-state-changed', extension);
let params = { version_tag: this._version_tag,
shell_version: Config.PACKAGE_VERSION,
api_version: API_VERSION.toString() };
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());
}
});

45
js/ui/flashspot.js Normal file
View File

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

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Shell = imports.gi.Shell;
@ -10,11 +10,9 @@ const Params = imports.misc.params;
const ICON_SIZE = 48;
function BaseIcon(label, createIcon) {
this._init(label, createIcon);
}
const BaseIcon = new Lang.Class({
Name: 'BaseIcon',
BaseIcon.prototype = {
_init : function(label, params) {
params = Params.parse(params, { createIcon: null,
setSizeManually: false,
@ -37,7 +35,8 @@ BaseIcon.prototype = {
this.actor.set_child(box);
this.iconSize = ICON_SIZE;
this._iconBin = new St.Bin();
this._iconBin = new St.Bin({ x_align: St.Align.MIDDLE,
y_align: St.Align.MIDDLE });
box.add_actor(this._iconBin);
@ -127,12 +126,12 @@ BaseIcon.prototype = {
this.iconSize = size;
this.icon = this.createIcon(this.iconSize);
this._iconBin.child = this.icon;
// The icon returned by createIcon() might actually be smaller than
// the requested icon size (for instance StTextureCache does this
// for fallback icons), so set the size explicitly.
this.icon.set_size(this.iconSize, this.iconSize);
this._iconBin.child = this.icon;
this._iconBin.set_size(this.iconSize, this.iconSize);
},
_onStyleChanged: function() {
@ -147,15 +146,18 @@ BaseIcon.prototype = {
size = found ? len : ICON_SIZE;
}
// don't create icons unnecessarily
if (size == this.iconSize &&
this._iconBin.child)
return;
this._createIconTexture(size);
}
};
});
function IconGrid(params) {
this._init(params);
}
const IconGrid = new Lang.Class({
Name: 'IconGrid',
IconGrid.prototype = {
_init: function(params) {
params = Params.parse(params, { rowLimit: null,
columnLimit: null,
@ -303,7 +305,7 @@ IconGrid.prototype = {
_onStyleChanged: function() {
let themeNode = this.actor.get_theme_node();
this._spacing = themeNode.get_length('spacing');
this._item_size = themeNode.get_length('-shell-grid-item-size');
this._item_size = themeNode.get_length('-shell-grid-item-size') || ICON_SIZE;
this._grid.queue_relayout();
},
@ -324,4 +326,4 @@ IconGrid.prototype = {
visibleItemsCount: function() {
return this._grid.get_children().length - this._grid.get_n_skip_paint();
}
};
});

563
js/ui/keyboard.js Normal file
View File

@ -0,0 +1,563 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Caribou = imports.gi.Caribou;
const Clutter = imports.gi.Clutter;
const DBus = imports.dbus;
const Gdk = imports.gi.Gdk;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const BoxPointer = imports.ui.boxpointer;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const KEYBOARD_SCHEMA = 'org.gnome.shell.keyboard';
const KEYBOARD_TYPE = 'keyboard-type';
const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications';
const SHOW_KEYBOARD = 'screen-keyboard-enabled';
// Key constants taken from Antler
// FIXME: ought to be moved into libcaribou
const PRETTY_KEYS = {
'BackSpace': '\u232b',
'space': ' ',
'Return': '\u23ce',
'Caribou_Prefs': '\u2328',
'Caribou_ShiftUp': '\u2b06',
'Caribou_ShiftDown': '\u2b07',
'Caribou_Emoticons': '\u263a',
'Caribou_Symbols': '123',
'Caribou_Symbols_More': '{#*',
'Caribou_Alpha': 'Abc',
'Tab': 'Tab',
'Escape': 'Esc',
'Control_L': 'Ctrl',
'Alt_L': 'Alt'
};
const CaribouKeyboardIface = <interface name='org.gnome.Caribou.Keyboard'>
<method name='Show'>
<arg type='u' direction='in' />
</method>
<method name='Hide'>
<arg type='u' direction='in' />
</method>
<method name='SetCursorLocation'>
<arg type='i' direction='in' />
<arg type='i' direction='in' />
<arg type='i' direction='in' />
<arg type='i' direction='in' />
</method>
<method name='SetEntryLocation'>
<arg type='i' direction='in' />
<arg type='i' direction='in' />
<arg type='i' direction='in' />
<arg type='i' direction='in' />
</method>
<property name='Name' access='read' type='s' />
</interface>;
const Key = new Lang.Class({
Name: 'Key',
_init : function(key) {
this._key = key;
this.actor = this._makeKey();
this._extended_keys = this._key.get_extended_keys();
this._extended_keyboard = null;
if (this._key.name == 'Control_L' || this._key.name == 'Alt_L')
this._key.latch = true;
this._key.connect('key-pressed', Lang.bind(this, function ()
{ this.actor.checked = true }));
this._key.connect('key-released', Lang.bind(this, function ()
{ this.actor.checked = false; }));
if (this._extended_keys.length > 0) {
this._grabbed = false;
this._eventCaptureId = 0;
this._key.connect('notify::show-subkeys', Lang.bind(this, this._onShowSubkeysChanged));
this._boxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
{ x_fill: true,
y_fill: true,
x_align: St.Align.START });
// Adds style to existing keyboard style to avoid repetition
this._boxPointer.actor.add_style_class_name('keyboard-subkeys');
this._getExtendedKeys();
this.actor._extended_keys = this._extended_keyboard;
this._boxPointer.actor.hide();
Main.layoutManager.addChrome(this._boxPointer.actor, { visibleInFullscreen: true });
}
},
_makeKey: function () {
let label = this._key.name;
if (label.length > 1) {
let pretty = PRETTY_KEYS[label];
if (pretty)
label = pretty;
else
label = this._getUnichar(this._key);
}
label = GLib.markup_escape_text(label, -1);
let button = new St.Button ({ label: label,
style_class: 'keyboard-key' });
button.key_width = this._key.width;
button.connect('button-press-event', Lang.bind(this, function () { this._key.press(); }));
button.connect('button-release-event', Lang.bind(this, function () { this._key.release(); }));
return button;
},
_getUnichar: function(key) {
let keyval = key.keyval;
let unichar = Gdk.keyval_to_unicode(keyval);
if (unichar) {
return String.fromCharCode(unichar);
} else {
return key.name;
}
},
_getExtendedKeys: function () {
this._extended_keyboard = new St.BoxLayout({ style_class: 'keyboard-layout',
vertical: false });
for (let i = 0; i < this._extended_keys.length; ++i) {
let extended_key = this._extended_keys[i];
let label = this._getUnichar(extended_key);
let key = new St.Button({ label: label, style_class: 'keyboard-key' });
key.extended_key = extended_key;
key.connect('button-press-event', Lang.bind(this, function () { extended_key.press(); }));
key.connect('button-release-event', Lang.bind(this, function () { extended_key.release(); }));
this._extended_keyboard.add(key);
}
this._boxPointer.bin.add_actor(this._extended_keyboard);
},
_onEventCapture: function (actor, event) {
let source = event.get_source();
let type = event.type();
if ((type == Clutter.EventType.BUTTON_PRESS ||
type == Clutter.EventType.BUTTON_RELEASE) &&
this._extended_keyboard.contains(source)) {
source.extended_key.press();
source.extended_key.release();
return false;
}
if (type == Clutter.EventType.BUTTON_PRESS) {
this._boxPointer.actor.hide();
this._ungrab();
return true;
}
return false;
},
_ungrab: function () {
global.stage.disconnect(this._eventCaptureId);
this._eventCaptureId = 0;
this._grabbed = false;
Main.popModal(this.actor);
},
_onShowSubkeysChanged: function () {
if (this._key.show_subkeys) {
this.actor.fake_release();
this._boxPointer.actor.raise_top();
this._boxPointer.setPosition(this.actor, 0.5);
this._boxPointer.show(true);
this.actor.set_hover(false);
if (!this._grabbed) {
Main.pushModal(this.actor);
this._eventCaptureId = global.stage.connect('captured-event', Lang.bind(this, this._onEventCapture));
this._grabbed = true;
}
this._key.release();
} else {
if (this._grabbed)
this._ungrab();
this._boxPointer.hide(true);
}
}
});
const Keyboard = new Lang.Class({
// HACK: we can't set Name, because it collides with Name dbus property
// Name: 'Keyboard',
_init: function () {
this._impl = Gio.DBusExportedObject.wrapJSObject(CaribouKeyboardIface, this);
this._impl.export(Gio.DBus.session, '/org/gnome/Caribou/Keyboard');
this.actor = null;
this._timestamp = global.get_current_time();
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
this._keyboardSettings = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
this._keyboardSettings.connect('changed', Lang.bind(this, this._settingsChanged));
this._a11yApplicationsSettings = new Gio.Settings({ schema: A11Y_APPLICATIONS_SCHEMA });
this._a11yApplicationsSettings.connect('changed', Lang.bind(this, this._settingsChanged));
this._settingsChanged();
},
init: function () {
this._redraw();
},
_settingsChanged: function (settings, key) {
this._enableKeyboard = this._a11yApplicationsSettings.get_boolean(SHOW_KEYBOARD);
if (!this._enableKeyboard && !this._keyboard)
return;
if (this._enableKeyboard && this._keyboard &&
this._keyboard.keyboard_type == this._keyboardSettings.get_string(KEYBOARD_TYPE))
return;
if (this._keyboard)
this._destroyKeyboard();
if (this._enableKeyboard) {
// If we've been called because the setting actually just
// changed to true (as opposed to being called from
// this._init()), then we want to pop up the keyboard.
let showKeyboard = (settings != null);
// However, caribou-gtk-module or this._onKeyFocusChanged
// will probably immediately tell us to hide it, so we
// have to fake things out so we'll ignore that request.
if (showKeyboard)
this._timestamp = global.display.get_current_time_roundtrip() + 1;
this._setupKeyboard(showKeyboard);
} else
Main.layoutManager.hideKeyboard(true);
},
_destroyKeyboard: function() {
if (this._keyboardNotifyId)
this._keyboard.disconnect(this._keyboardNotifyId);
if (this._focusNotifyId)
global.stage.disconnect(this._focusNotifyId);
this._keyboard = null;
this.actor.destroy();
this.actor = null;
this._destroySource();
},
_setupKeyboard: function(show) {
this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true, reactive: true });
Main.layoutManager.keyboardBox.add_actor(this.actor);
Main.layoutManager.trackChrome(this.actor);
this._keyboard = new Caribou.KeyboardModel({ keyboard_type: this._keyboardSettings.get_string(KEYBOARD_TYPE) });
this._groups = {};
this._current_page = null;
// Initialize keyboard key measurements
this._numOfHorizKeys = 0;
this._numOfVertKeys = 0;
this._addKeys();
this._keyboardNotifyId = this._keyboard.connect('notify::active-group', Lang.bind(this, this._onGroupChanged));
this._focusNotifyId = global.stage.connect('notify::key-focus', Lang.bind(this, this._onKeyFocusChanged));
if (show)
this.show();
else
this._createSource();
},
_onKeyFocusChanged: function () {
let focus = global.stage.key_focus;
// Showing an extended key popup and clicking a key from the extended keys
// will grab focus, but ignore that
if (focus && (focus._extended_keys || (focus._key && focus._key.extended_key)))
return;
let time = global.get_current_time();
if (focus instanceof Clutter.Text)
this.Show(time);
else
this.Hide(time);
},
_addKeys: function () {
let groups = this._keyboard.get_groups();
for (let i = 0; i < groups.length; ++i) {
let gname = groups[i];
let group = this._keyboard.get_group(gname);
group.connect('notify::active-level', Lang.bind(this, this._onLevelChanged));
let layers = {};
let levels = group.get_levels();
for (let j = 0; j < levels.length; ++j) {
let lname = levels[j];
let level = group.get_level(lname);
let layout = new St.BoxLayout({ style_class: 'keyboard-layout',
vertical: true });
this._loadRows(level, layout);
layers[lname] = layout;
this.actor.add(layout, { x_fill: false });
layout.hide();
}
this._groups[gname] = layers;
}
this._setActiveLayer();
},
_getTrayIcon: function () {
let trayButton = new St.Button ({ label: _("tray"),
style_class: 'keyboard-key' });
trayButton.key_width = 1;
trayButton.connect('button-press-event', Lang.bind(this, function () {
Main.messageTray.toggle();
}));
Main.overview.connect('showing', Lang.bind(this, function () {
trayButton.reactive = false;
trayButton.add_style_pseudo_class('grayed');
}));
Main.overview.connect('hiding', Lang.bind(this, function () {
trayButton.reactive = true;
trayButton.remove_style_pseudo_class('grayed');
}));
return trayButton;
},
_addRows : function (keys, layout) {
let keyboard_row = new St.BoxLayout();
for (let i = 0; i < keys.length; ++i) {
let children = keys[i].get_children();
let right_box = new St.BoxLayout({ style_class: 'keyboard-row' });
let left_box = new St.BoxLayout({ style_class: 'keyboard-row' });
for (let j = 0; j < children.length; ++j) {
if (this._numOfHorizKeys == 0)
this._numOfHorizKeys = children.length;
let key = children[j];
let button = new Key(key);
if (key.align == 'right')
right_box.add(button.actor);
else
left_box.add(button.actor);
if (key.name == 'Caribou_Prefs') {
key.connect('key-released', Lang.bind(this, this.hide));
// Add new key for hiding message tray
right_box.add(this._getTrayIcon());
}
}
keyboard_row.add(left_box, { expand: true, x_fill: false, x_align: St.Align.START });
keyboard_row.add(right_box, { expand: true, x_fill: false, x_align: St.Align.END });
}
layout.add(keyboard_row);
},
_loadRows : function (level, layout) {
let rows = level.get_rows();
for (let i = 0; i < rows.length; ++i) {
let row = rows[i];
if (this._numOfVertKeys == 0)
this._numOfVertKeys = rows.length;
this._addRows(row.get_columns(), layout);
}
},
_redraw: function () {
if (!this._enableKeyboard)
return;
let monitor = Main.layoutManager.bottomMonitor;
let maxHeight = monitor.height / 3;
this.actor.width = monitor.width;
let layout = this._current_page;
let verticalSpacing = layout.get_theme_node().get_length('spacing');
let padding = layout.get_theme_node().get_length('padding');
let box = layout.get_children()[0].get_children()[0];
let horizontalSpacing = box.get_theme_node().get_length('spacing');
let allHorizontalSpacing = (this._numOfHorizKeys - 1) * horizontalSpacing;
let keyWidth = Math.floor((this.actor.width - allHorizontalSpacing - 2 * padding) / this._numOfHorizKeys);
let allVerticalSpacing = (this._numOfVertKeys - 1) * verticalSpacing;
let keyHeight = Math.floor((maxHeight - allVerticalSpacing - 2 * padding) / this._numOfVertKeys);
let keySize = Math.min(keyWidth, keyHeight);
this.actor.height = keySize * this._numOfVertKeys + allVerticalSpacing + 2 * padding;
let rows = this._current_page.get_children();
for (let i = 0; i < rows.length; ++i) {
let keyboard_row = rows[i];
let boxes = keyboard_row.get_children();
for (let j = 0; j < boxes.length; ++j) {
let keys = boxes[j].get_children();
for (let k = 0; k < keys.length; ++k) {
let child = keys[k];
child.width = keySize * child.key_width;
child.height = keySize;
if (child._extended_keys) {
let extended_keys = child._extended_keys.get_children();
for (let n = 0; n < extended_keys.length; ++n) {
let extended_key = extended_keys[n];
extended_key.width = keySize;
extended_key.height = keySize;
}
}
}
}
}
},
_onLevelChanged: function () {
this._setActiveLayer();
this._redraw();
},
_onGroupChanged: function () {
this._setActiveLayer();
this._redraw();
},
_setActiveLayer: function () {
let active_group_name = this._keyboard.active_group;
let active_group = this._keyboard.get_group(active_group_name);
let active_level = active_group.active_level;
let layers = this._groups[active_group_name];
if (this._current_page != null) {
this._current_page.hide();
}
this._current_page = layers[active_level];
this._current_page.show();
},
_createSource: function () {
if (this._source == null) {
this._source = new KeyboardSource(this);
this._source.setTransient(true);
Main.messageTray.add(this._source);
}
},
_destroySource: function () {
if (this._source) {
this._source.destroy();
this._source = null;
}
},
show: function () {
this._redraw();
Main.layoutManager.showKeyboard();
this._destroySource();
},
hide: function () {
Main.layoutManager.hideKeyboard();
this._createSource();
},
_moveTemporarily: function () {
let currentWindow = global.screen.get_display().focus_window;
let rect = currentWindow.get_outer_rect();
let newX = rect.x;
let newY = 3 * this.actor.height / 2;
currentWindow.move_frame(true, newX, newY);
},
_setLocation: function (x, y) {
if (y >= 2 * this.actor.height)
this._moveTemporarily();
},
// D-Bus methods
Show: function(timestamp) {
if (!this._enableKeyboard)
return;
if (timestamp - this._timestamp < 0)
return;
this._timestamp = timestamp;
this.show();
},
Hide: function(timestamp) {
if (!this._enableKeyboard)
return;
if (timestamp - this._timestamp < 0)
return;
this._timestamp = timestamp;
this.hide();
},
SetCursorLocation: function(x, y, w, h) {
if (!this._enableKeyboard)
return;
// this._setLocation(x, y);
},
SetEntryLocation: function(x, y, w, h) {
if (!this._enableKeyboard)
return;
// this._setLocation(x, y);
},
get Name() {
return 'gnome-shell';
}
});
const KeyboardSource = new Lang.Class({
Name: 'KeyboardSource',
Extends: MessageTray.Source,
_init: function(keyboard) {
this.parent(_("Keyboard"));
this._keyboard = keyboard;
this._setSummaryIcon(this.createNotificationIcon());
},
createNotificationIcon: function() {
return new St.Icon({ icon_name: 'input-keyboard',
icon_type: St.IconType.SYMBOLIC,
icon_size: this.ICON_SIZE });
},
handleSummaryClick: function() {
let event = Clutter.get_current_event();
if (event.type() != Clutter.EventType.BUTTON_RELEASE)
return false;
this.open();
return true;
},
open: function() {
this._keyboard.show();
}
});

View File

@ -1,36 +1,66 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const Main = imports.ui.main;
const Params = imports.misc.params;
const ScreenSaver = imports.misc.screenSaver;
const Tweener = imports.ui.tweener;
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
const STARTUP_ANIMATION_TIME = 0.2;
const KEYBOARD_ANIMATION_TIME = 0.5;
function LayoutManager() {
this._init.apply(this, arguments);
}
const LayoutManager = new Lang.Class({
Name: 'LayoutManager',
LayoutManager.prototype = {
_init: function () {
this._rtl = (St.Widget.get_default_direction() == St.TextDirection.RTL);
this.monitors = [];
this.primaryMonitor = null;
this.primaryIndex = -1;
this._hotCorners = [];
this._leftPanelBarrier = 0;
this._rightPanelBarrier = 0;
this._trayBarrier = 0;
this._updateMonitors();
this._chrome = new Chrome(this);
this.panelBox = new St.BoxLayout({ name: 'panelBox',
vertical: true });
this.addChrome(this.panelBox, { affectsStruts: true });
this.panelBox.connect('allocation-changed',
Lang.bind(this, this._updatePanelBarriers));
this.trayBox = new St.BoxLayout({ name: 'trayBox' });
this.addChrome(this.trayBox, { visibleInFullscreen: true });
this.trayBox.connect('allocation-changed',
Lang.bind(this, this._updateTrayBarrier));
this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox',
reactive: true,
track_hover: true });
this.addChrome(this.keyboardBox, { visibleInFullscreen: true });
this._keyboardHeightNotifyId = 0;
global.screen.connect('monitors-changed',
Lang.bind(this, this._monitorsChanged));
this._monitorsChanged();
},
// This is called by Main after everything else is constructed;
// _updateHotCorners needs access to Main.panel, which didn't exist
// Chrome.init() needs access to Main.overview, which didn't exist
// yet when the LayoutManager was constructed.
init: function() {
global.screen.connect('monitors-changed', Lang.bind(this, this._monitorsChanged));
this._updateHotCorners();
this._chrome.init();
this._startupAnimation();
},
_updateMonitors: function() {
@ -109,12 +139,71 @@ LayoutManager.prototype = {
let corner = new HotCorner();
this._hotCorners.push(corner);
corner.actor.set_position(cornerX, cornerY);
Main.chrome.addActor(corner.actor, { affectsStruts: false });
this._chrome.addActor(corner.actor);
}
},
_updateBoxes: function() {
this.panelBox.set_position(this.primaryMonitor.x, this.primaryMonitor.y);
this.panelBox.set_size(this.primaryMonitor.width, -1);
this.keyboardBox.set_position(this.bottomMonitor.x,
this.bottomMonitor.y + this.bottomMonitor.height);
this.keyboardBox.set_size(this.bottomMonitor.width, -1);
this.trayBox.set_position(this.bottomMonitor.x,
this.bottomMonitor.y + this.bottomMonitor.height);
this.trayBox.set_size(this.bottomMonitor.width, -1);
// Set trayBox's clip to show things above it, but not below
// it (so it's not visible behind the keyboard). The exact
// height of the clip doesn't matter, as long as it's taller
// than any Notification.actor.
this.trayBox.set_clip(0, -this.bottomMonitor.height,
this.bottomMonitor.width, this.bottomMonitor.height);
},
_updatePanelBarriers: function() {
if (this._leftPanelBarrier)
global.destroy_pointer_barrier(this._leftPanelBarrier);
if (this._rightPanelBarrier)
global.destroy_pointer_barrier(this._rightPanelBarrier);
if (this.panelBox.height) {
let primary = this.primaryMonitor;
this._leftPanelBarrier =
global.create_pointer_barrier(primary.x, primary.y,
primary.x, primary.y + this.panelBox.height,
1 /* BarrierPositiveX */);
this._rightPanelBarrier =
global.create_pointer_barrier(primary.x + primary.width, primary.y,
primary.x + primary.width, primary.y + this.panelBox.height,
4 /* BarrierNegativeX */);
} else {
this._leftPanelBarrier = 0;
this._rightPanelBarrier = 0;
}
},
_updateTrayBarrier: function() {
let monitor = this.bottomMonitor;
if (this._trayBarrier)
global.destroy_pointer_barrier(this._trayBarrier);
if (Main.messageTray) {
this._trayBarrier =
global.create_pointer_barrier(monitor.x + monitor.width, monitor.y + monitor.height - Main.messageTray.actor.height,
monitor.x + monitor.width, monitor.y + monitor.height,
4 /* BarrierNegativeX */);
} else {
this._trayBarrier = 0;
}
},
_monitorsChanged: function() {
this._updateMonitors();
this._updateBoxes();
this._updateHotCorners();
this.emit('monitors-changed');
@ -125,19 +214,17 @@ LayoutManager.prototype = {
let monitorLeft = monitor.x, monitorRight = monitor.x + monitor.width;
let primaryLeft = primary.x, primaryRight = primary.x + primary.width;
if ((monitorLeft >= primaryLeft && monitorLeft <= primaryRight) ||
(monitorRight >= primaryLeft && monitorRight <= primaryRight) ||
(primaryLeft >= monitorLeft && primaryLeft <= monitorRight) ||
(primaryRight >= monitorLeft && primaryRight <= monitorRight))
if ((monitorLeft >= primaryLeft && monitorLeft < primaryRight) ||
(monitorRight > primaryLeft && monitorRight <= primaryRight) ||
(primaryLeft >= monitorLeft && primaryLeft < monitorRight) ||
(primaryRight > monitorLeft && primaryRight <= monitorRight))
return true;
return false;
},
get focusIndex() {
let screen = global.screen;
let display = screen.get_display();
let focusWindow = display.focus_window;
let focusWindow = global.display.focus_window;
if (focusWindow) {
let wrect = focusWindow.get_outer_rect();
@ -156,8 +243,136 @@ LayoutManager.prototype = {
get focusMonitor() {
return this.monitors[this.focusIndex];
},
_startupAnimation: function() {
// Don't animate the strut
this._chrome.freezeUpdateRegions();
this.panelBox.anchor_y = this.panelBox.height;
Tweener.addTween(this.panelBox,
{ anchor_y: 0,
time: STARTUP_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._startupAnimationComplete,
onCompleteScope: this
});
},
_startupAnimationComplete: function() {
this._chrome.thawUpdateRegions();
},
showKeyboard: function () {
Main.messageTray.hide();
this.keyboardBox.raise_top();
Tweener.addTween(this.keyboardBox,
{ anchor_y: this.keyboardBox.height,
time: KEYBOARD_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._showKeyboardComplete,
onCompleteScope: this
});
Tweener.addTween(this.trayBox,
{ anchor_y: this.keyboardBox.height,
time: KEYBOARD_ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
_showKeyboardComplete: function() {
// Poke Chrome to update the input shape; it doesn't notice
// anchor point changes
this._chrome.updateRegions();
this._keyboardHeightNotifyId = this.keyboardBox.connect('notify::height', Lang.bind(this, function () {
this.keyboardBox.anchor_y = this.keyboardBox.height;
this.trayBox.anchor_y = this.keyboardBox.height;
}));
},
hideKeyboard: function (immediate) {
Main.messageTray.hide();
if (this._keyboardHeightNotifyId) {
this.keyboardBox.disconnect(this._keyboardHeightNotifyId);
this._keyboardHeightNotifyId = 0;
}
Tweener.addTween(this.keyboardBox,
{ anchor_y: 0,
time: immediate ? 0 : KEYBOARD_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: this._hideKeyboardComplete,
onCompleteScope: this
});
Tweener.addTween(this.trayBox,
{ anchor_y: 0,
time: immediate ? 0 : KEYBOARD_ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
_hideKeyboardComplete: function() {
this._chrome.updateRegions();
},
// addChrome:
// @actor: an actor to add to the chrome
// @params: (optional) additional params
//
// Adds @actor to the chrome, and (unless %affectsInputRegion in
// @params is %false) extends the input region to include it.
// Changes in @actor's size, position, and visibility will
// automatically result in appropriate changes to the input
// region.
//
// If %affectsStruts in @params is %true (and @actor is along a
// screen edge), then @actor's size and position will also affect
// the window manager struts. Changes to @actor's visibility will
// NOT affect whether or not the strut is present, however.
//
// If %visibleInFullscreen in @params is %true, the actor will be
// visible even when a fullscreen window should be covering it.
addChrome: function(actor, params) {
this._chrome.addActor(actor, params);
},
// trackChrome:
// @actor: a descendant of the chrome to begin tracking
// @params: parameters describing how to track @actor
//
// Tells the chrome to track @actor, which must be a descendant
// of an actor added via addChrome(). This can be used to extend the
// struts or input region to cover specific children.
//
// @params can have any of the same values as in addChrome(),
// though some possibilities don't make sense (eg, trying to have
// a %visibleInFullscreen child of a non-%visibleInFullscreen
// parent). By default, @actor has the same params as its chrome
// ancestor.
trackChrome: function(actor, params) {
this._chrome.trackActor(actor, params);
},
// untrackChrome:
// @actor: an actor previously tracked via trackChrome()
//
// Undoes the effect of trackChrome()
untrackChrome: function(actor) {
this._chrome.untrackActor(actor);
},
// removeChrome:
// @actor: a chrome actor
//
// Removes @actor from the chrome
removeChrome: function(actor) {
this._chrome.removeActor(actor);
},
findMonitorForActor: function(actor) {
return this._chrome.findMonitorForActor(actor);
}
};
});
Signals.addSignalMethods(LayoutManager.prototype);
@ -165,11 +380,9 @@ Signals.addSignalMethods(LayoutManager.prototype);
//
// This class manages a "hot corner" that can toggle switching to
// overview.
function HotCorner() {
this._init();
}
const HotCorner = new Lang.Class({
Name: 'HotCorner',
HotCorner.prototype = {
_init : function() {
// We use this flag to mark the case where the user has entered the
// hot corner and has not left both the hot corner and a surrounding
@ -218,13 +431,22 @@ HotCorner.prototype = {
Lang.bind(this, this._onCornerClicked));
this._corner.connect('leave-event',
Lang.bind(this, this._onCornerLeft));
// Cache the three ripples instead of dynamically creating and destroying them.
this._ripple1 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0, visible: false });
this._ripple2 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0, visible: false });
this._ripple3 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0, visible: false });
Main.uiGroup.add_actor(this._ripple1);
Main.uiGroup.add_actor(this._ripple2);
Main.uiGroup.add_actor(this._ripple3);
},
destroy: function() {
this.actor.destroy();
},
_addRipple : function(delay, time, startScale, startOpacity, finalScale, finalOpacity) {
_animRipple : function(ripple, delay, time, startScale, startOpacity, finalScale) {
// We draw a ripple by using a source image and animating it scaling
// outwards and fading away. We want the ripples to move linearly
// or it looks unrealistic, but if the opacity of the ripple goes
@ -232,25 +454,27 @@ HotCorner.prototype = {
// 'onUpdate' to give a non-linear curve to the fade-away and make
// it more visible in the middle section.
let [x, y] = this._corner.get_transformed_position();
let ripple = new St.BoxLayout({ style_class: 'ripple-box',
opacity: 255 * Math.sqrt(startOpacity),
scale_x: startScale,
scale_y: startScale,
x: x,
y: y });
ripple._opacity = startOpacity;
ripple._opacity = startOpacity;
if (ripple.get_direction() == St.TextDirection.RTL)
ripple.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
Tweener.addTween(ripple, { _opacity: finalOpacity,
ripple.visible = true;
ripple.opacity = 255 * Math.sqrt(startOpacity);
ripple.scale_x = ripple.scale_y = startScale;
let [x, y] = this._corner.get_transformed_position();
ripple.x = x;
ripple.y = y;
Tweener.addTween(ripple, { _opacity: 0,
scale_x: finalScale,
scale_y: finalScale,
delay: delay,
time: time,
transition: 'linear',
onUpdate: function() { ripple.opacity = 255 * Math.sqrt(ripple._opacity); },
onComplete: function() { ripple.destroy(); } });
Main.uiGroup.add_actor(ripple);
onComplete: function() { ripple.visible = false; } });
},
rippleAnimation: function() {
@ -258,10 +482,10 @@ HotCorner.prototype = {
// parameters were found by trial and error, so don't look
// for them to make perfect sense mathematically
// delay time scale opacity => scale opacity
this._addRipple(0.0, 0.83, 0.25, 1.0, 1.5, 0.0);
this._addRipple(0.05, 1.0, 0.0, 0.7, 1.25, 0.0);
this._addRipple(0.35, 1.0, 0.0, 0.3, 1, 0.0);
// delay time scale opacity => scale
this._animRipple(this._ripple1, 0.0, 0.83, 0.25, 1.0, 1.5);
this._animRipple(this._ripple2, 0.05, 1.0, 0.0, 0.7, 1.25);
this._animRipple(this._ripple3, 0.35, 1.0, 0.0, 0.3, 1);
},
handleDragOver: function(source, actor, x, y, time) {
@ -320,4 +544,435 @@ HotCorner.prototype = {
return true;
return false;
}
});
// This manages the shell "chrome"; the UI that's visible in the
// normal mode (ie, outside the Overview), that surrounds the main
// workspace content.
const defaultParams = {
visibleInFullscreen: false,
affectsStruts: false,
affectsInputRegion: true
};
const Chrome = new Lang.Class({
Name: 'Chrome',
_init: function(layoutManager) {
this._layoutManager = layoutManager;
this._monitors = [];
this._inOverview = false;
this._updateRegionIdle = 0;
this._freezeUpdateCount = 0;
this._trackedActors = [];
this._layoutManager.connect('monitors-changed',
Lang.bind(this, this._relayout));
global.screen.connect('restacked',
Lang.bind(this, this._windowsRestacked));
// Need to update struts on new workspaces when they are added
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._queueUpdateRegions));
this._screenSaverActive = false;
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
this._screenSaverProxy.connectSignal('ActiveChanged', Lang.bind(this, function(proxy, senderName, [isActive]) {
this._onScreenSaverActiveChanged(isActive);
}));
this._screenSaverProxy.GetActiveRemote(Lang.bind(this, function(result, err) {
if (!err)
this._onScreenSaverActiveChanged(result[0]);
}));
this._relayout();
},
init: function() {
Main.overview.connect('showing',
Lang.bind(this, this._overviewShowing));
Main.overview.connect('hidden',
Lang.bind(this, this._overviewHidden));
},
addActor: function(actor, params) {
Main.uiGroup.add_actor(actor);
this._trackActor(actor, params);
},
trackActor: function(actor, params) {
let ancestor = actor.get_parent();
let index = this._findActor(ancestor);
while (ancestor && index == -1) {
ancestor = ancestor.get_parent();
index = this._findActor(ancestor);
}
if (!ancestor)
throw new Error('actor is not a descendent of a chrome actor');
let ancestorData = this._trackedActors[index];
if (!params)
params = {};
// We can't use Params.parse here because we want to drop
// the extra values like ancestorData.actor
for (let prop in defaultParams) {
if (!params.hasOwnProperty(prop))
params[prop] = ancestorData[prop];
}
this._trackActor(actor, params);
},
untrackActor: function(actor) {
this._untrackActor(actor);
},
removeActor: function(actor) {
Main.uiGroup.remove_actor(actor);
this._untrackActor(actor);
},
_findActor: function(actor) {
for (let i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i];
if (actorData.actor == actor)
return i;
}
return -1;
},
_trackActor: function(actor, params) {
if (this._findActor(actor) != -1)
throw new Error('trying to re-track existing chrome actor');
let actorData = Params.parse(params, defaultParams);
actorData.actor = actor;
actorData.isToplevel = actor.get_parent() == Main.uiGroup;
actorData.visibleId = actor.connect('notify::visible',
Lang.bind(this, this._queueUpdateRegions));
actorData.allocationId = actor.connect('notify::allocation',
Lang.bind(this, this._queueUpdateRegions));
actorData.parentSetId = actor.connect('parent-set',
Lang.bind(this, this._actorReparented));
// Note that destroying actor will unset its parent, so we don't
// need to connect to 'destroy' too.
this._trackedActors.push(actorData);
this._queueUpdateRegions();
},
_untrackActor: function(actor) {
let i = this._findActor(actor);
if (i == -1)
return;
let actorData = this._trackedActors[i];
this._trackedActors.splice(i, 1);
actor.disconnect(actorData.visibleId);
actor.disconnect(actorData.allocationId);
actor.disconnect(actorData.parentSetId);
this._queueUpdateRegions();
},
_actorReparented: function(actor, oldParent) {
let newParent = actor.get_parent();
if (!newParent)
this._untrackActor(actor);
else
actorData.isToplevel = (newParent == Main.uiGroup);
},
_updateVisibility: function() {
for (let i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i], visible;
if (!actorData.isToplevel)
continue;
if (this._screenSaverActive)
visible = false;
else if (this._inOverview)
visible = true;
else if (!actorData.visibleInFullscreen &&
this.findMonitorForActor(actorData.actor).inFullscreen)
visible = false;
else
visible = true;
Main.uiGroup.set_skip_paint(actorData.actor, !visible);
}
},
_overviewShowing: function() {
this._inOverview = true;
this._updateVisibility();
this._queueUpdateRegions();
},
_overviewHidden: function() {
this._inOverview = false;
this._updateVisibility();
this._queueUpdateRegions();
},
_relayout: function() {
this._monitors = this._layoutManager.monitors;
this._primaryMonitor = this._layoutManager.primaryMonitor;
this._updateFullscreen();
this._updateVisibility();
this._queueUpdateRegions();
},
_onScreenSaverActiveChanged: function(screenSaverActive) {
this._screenSaverActive = screenSaverActive;
this._updateVisibility();
this._queueUpdateRegions();
},
_findMonitorForRect: function(x, y, w, h) {
// First look at what monitor the center of the rectangle is at
let cx = x + w/2;
let cy = y + h/2;
for (let i = 0; i < this._monitors.length; i++) {
let monitor = this._monitors[i];
if (cx >= monitor.x && cx < monitor.x + monitor.width &&
cy >= monitor.y && cy < monitor.y + monitor.height)
return monitor;
}
// If the center is not on a monitor, return the first overlapping monitor
for (let i = 0; i < this._monitors.length; i++) {
let monitor = this._monitors[i];
if (x + w > monitor.x && x < monitor.x + monitor.width &&
y + h > monitor.y && y < monitor.y + monitor.height)
return monitor;
}
// otherwise on no monitor
return null;
},
_findMonitorForWindow: function(window) {
return this._findMonitorForRect(window.x, window.y, window.width, window.height);
},
// This call guarantees that we return some monitor to simplify usage of it
// In practice all tracked actors should be visible on some monitor anyway
findMonitorForActor: function(actor) {
let [x, y] = actor.get_transformed_position();
let [w, h] = actor.get_transformed_size();
let monitor = this._findMonitorForRect(x, y, w, h);
if (monitor)
return monitor;
return this._primaryMonitor; // Not on any monitor, pretend its on the primary
},
_queueUpdateRegions: function() {
if (!this._updateRegionIdle && !this._freezeUpdateCount)
this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this.updateRegions),
Meta.PRIORITY_BEFORE_REDRAW);
},
freezeUpdateRegions: function() {
if (this._updateRegionIdle)
this.updateRegions();
this._freezeUpdateCount++;
},
thawUpdateRegions: function() {
this._freezeUpdateCount--;
this._queueUpdateRegions();
},
_updateFullscreen: function() {
let windows = Main.getWindowActorsForWorkspace(global.screen.get_active_workspace_index());
// Reset all monitors to not fullscreen
for (let i = 0; i < this._monitors.length; i++)
this._monitors[i].inFullscreen = false;
// Ordinary chrome should be visible unless there is a window
// with layer FULLSCREEN, or a window with layer
// OVERRIDE_REDIRECT that covers the whole screen.
// ('override_redirect' is not actually a layer above all
// other windows, but this seems to be how mutter treats it
// currently...) If we wanted to be extra clever, we could
// figure out when an OVERRIDE_REDIRECT window was trying to
// partially overlap us, and then adjust the input region and
// our clip region accordingly...
// @windows is sorted bottom to top.
for (let i = windows.length - 1; i > -1; i--) {
let window = windows[i];
let layer = window.get_meta_window().get_layer();
// Skip minimized windows
if (!window.showing_on_its_workspace())
continue;
if (layer == Meta.StackLayer.FULLSCREEN) {
let monitor = this._findMonitorForWindow(window);
if (monitor)
monitor.inFullscreen = true;
}
if (layer == Meta.StackLayer.OVERRIDE_REDIRECT) {
// Check whether the window is screen sized
let isScreenSized =
(window.x == 0 && window.y == 0 &&
window.width == global.screen_width &&
window.height == global.screen_height);
if (isScreenSized) {
for (let i = 0; i < this._monitors.length; i++)
this._monitors[i].inFullscreen = true;
}
// Or whether it is monitor sized
let monitor = this._findMonitorForWindow(window);
if (monitor &&
window.x <= monitor.x &&
window.x + window.width >= monitor.x + monitor.width &&
window.y <= monitor.y &&
window.y + window.height >= monitor.y + monitor.height)
monitor.inFullscreen = true;
} else
break;
}
},
_windowsRestacked: function() {
let wasInFullscreen = [];
for (let i = 0; i < this._monitors.length; i++)
wasInFullscreen[i] = this._monitors[i].inFullscreen;
this._updateFullscreen();
let changed = false;
for (let i = 0; i < wasInFullscreen.length; i++) {
if (wasInFullscreen[i] != this._monitors[i].inFullscreen) {
changed = true;
break;
}
}
if (changed) {
this._updateVisibility();
this._queueUpdateRegions();
}
},
updateRegions: function() {
let rects = [], struts = [], i;
if (this._updateRegionIdle) {
Mainloop.source_remove(this._updateRegionIdle);
delete this._updateRegionIdle;
}
for (i = 0; i < this._trackedActors.length; i++) {
let actorData = this._trackedActors[i];
if (!actorData.affectsInputRegion && !actorData.affectsStruts)
continue;
let [x, y] = actorData.actor.get_transformed_position();
let [w, h] = actorData.actor.get_transformed_size();
x = Math.round(x);
y = Math.round(y);
w = Math.round(w);
h = Math.round(h);
let rect = new Meta.Rectangle({ x: x, y: y, width: w, height: h});
if (actorData.affectsInputRegion &&
actorData.actor.get_paint_visibility() &&
!Main.uiGroup.get_skip_paint(actorData.actor))
rects.push(rect);
if (!actorData.affectsStruts)
continue;
// Limit struts to the size of the screen
let x1 = Math.max(x, 0);
let x2 = Math.min(x + w, global.screen_width);
let y1 = Math.max(y, 0);
let y2 = Math.min(y + h, global.screen_height);
// NetWM struts are not really powerful enought to handle
// a multi-monitor scenario, they only describe what happens
// around the outer sides of the full display region. However
// it can describe a partial region along each side, so
// we can support having the struts only affect the
// primary monitor. This should be enough as we only have
// chrome affecting the struts on the primary monitor so
// far.
//
// Metacity wants to know what side of the screen the
// strut is considered to be attached to. If the actor is
// only touching one edge, or is touching the entire
// border of the primary monitor, then it's obvious which
// side to call it. If it's in a corner, we pick a side
// arbitrarily. If it doesn't touch any edges, or it spans
// the width/height across the middle of the screen, then
// we don't create a strut for it at all.
let side;
let primary = this._primaryMonitor;
if (x1 <= primary.x && x2 >= primary.x + primary.width) {
if (y1 <= primary.y)
side = Meta.Side.TOP;
else if (y2 >= primary.y + primary.height)
side = Meta.Side.BOTTOM;
else
continue;
} else if (y1 <= primary.y && y2 >= primary.y + primary.height) {
if (x1 <= 0)
side = Meta.Side.LEFT;
else if (x2 >= global.screen_width)
side = Meta.Side.RIGHT;
else
continue;
} else if (x1 <= 0)
side = Meta.Side.LEFT;
else if (y1 <= 0)
side = Meta.Side.TOP;
else if (x2 >= global.screen_width)
side = Meta.Side.RIGHT;
else if (y2 >= global.screen_height)
side = Meta.Side.BOTTOM;
else
continue;
// Ensure that the strut rects goes all the way to the screen edge,
// as this really what mutter expects.
switch (side) {
case Meta.Side.TOP:
y1 = 0;
break;
case Meta.Side.BOTTOM:
y2 = global.screen_height;
break;
case Meta.Side.LEFT:
x1 = 0;
break;
case Meta.Side.RIGHT:
x2 = global.screen_width;
break;
}
let strutRect = new Meta.Rectangle({ x: x1, y: y1, width: x2 - x1, height: y2 - y1});
let strut = new Meta.Strut({ rect: strutRect, side: side });
struts.push(strut);
}
global.set_stage_input_region(rects);
let screen = global.screen;
for (let w = 0; w < screen.n_workspaces; w++) {
let workspace = screen.get_workspace_by_index(w);
workspace.set_builtin_struts(struts);
}
return false;
}
});

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Meta = imports.gi.Meta;
@ -30,11 +30,9 @@ const Tweener = imports.ui.tweener;
* @container and will track any changes in its size. You can override
* this by passing an explicit width and height in @params.
*/
function Lightbox(container, params) {
this._init(container, params);
}
const Lightbox = new Lang.Class({
Name: 'Lightbox',
Lightbox.prototype = {
_init : function(container, params) {
params = Params.parse(params, { inhibitEvents: false,
width: null,
@ -196,4 +194,4 @@ Lightbox.prototype = {
this.highlight(null);
}
};
});

View File

@ -1,14 +1,12 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Signals = imports.signals;
const St = imports.gi.St;
function Link(props) {
this._init(props);
}
const Link = new Lang.Class({
Name: 'Link',
Link.prototype = {
_init : function(props) {
let realProps = { reactive: true,
track_hover: true,
@ -19,6 +17,5 @@ Link.prototype = {
this.actor = new St.Button(realProps);
}
};
});
Signals.addSignalMethods(Link.prototype);

View File

@ -1,11 +1,11 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Cogl = imports.gi.Cogl;
const GConf = imports.gi.GConf;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta;
const Pango = imports.gi.Pango;
const St = imports.gi.St;
const Shell = imports.gi.Shell;
@ -15,9 +15,12 @@ const Mainloop = imports.mainloop;
const History = imports.misc.history;
const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionUtils = imports.misc.extensionUtils;
const Link = imports.ui.link;
const ShellEntry = imports.ui.shellEntry;
const Tweener = imports.ui.tweener;
const Main = imports.ui.main;
const JsParse = imports.misc.jsParse;
/* Imports...feel free to add here as needed */
var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
@ -39,12 +42,88 @@ var commandHeader = 'const Clutter = imports.gi.Clutter; ' +
'const r = Lang.bind(Main.lookingGlass, Main.lookingGlass.getResult); ';
const HISTORY_KEY = 'looking-glass-history';
// Time between tabs for them to count as a double-tab event
const AUTO_COMPLETE_DOUBLE_TAB_DELAY = 500;
const AUTO_COMPLETE_SHOW_COMPLETION_ANIMATION_DURATION = 0.2;
const AUTO_COMPLETE_GLOBAL_KEYWORDS = _getAutoCompleteGlobalKeywords();
function Notebook() {
this._init();
function _getAutoCompleteGlobalKeywords() {
const keywords = ['true', 'false', 'null', 'new'];
// Don't add the private properties of window (i.e., ones starting with '_')
const windowProperties = Object.getOwnPropertyNames(window).filter(function(a){ return a.charAt(0) != '_' });
const headerProperties = JsParse.getDeclaredConstants(commandHeader);
return keywords.concat(windowProperties).concat(headerProperties);
}
Notebook.prototype = {
const AutoComplete = new Lang.Class({
Name: 'AutoComplete',
_init: function(entry) {
this._entry = entry;
this._entry.connect('key-press-event', Lang.bind(this, this._entryKeyPressEvent));
this._lastTabTime = global.get_current_time();
},
_processCompletionRequest: function(event) {
if (event.completions.length == 0) {
return;
}
// Unique match = go ahead and complete; multiple matches + single tab = complete the common starting string;
// multiple matches + double tab = emit a suggest event with all possible options
if (event.completions.length == 1) {
this.additionalCompletionText(event.completions[0], event.attrHead);
this.emit('completion', { completion: event.completions[0], type: 'whole-word' });
} else if (event.completions.length > 1 && event.tabType === 'single') {
let commonPrefix = JsParse.getCommonPrefix(event.completions);
if (commonPrefix.length > 0) {
this.additionalCompletionText(commonPrefix, event.attrHead);
this.emit('completion', { completion: commonPrefix, type: 'prefix' });
this.emit('suggest', { completions: event.completions});
}
} else if (event.completions.length > 1 && event.tabType === 'double') {
this.emit('suggest', { completions: event.completions});
}
},
_entryKeyPressEvent: function(actor, event) {
let cursorPos = this._entry.clutter_text.get_cursor_position();
let text = this._entry.get_text();
if (cursorPos != -1) {
text = text.slice(0, cursorPos);
}
if (event.get_key_symbol() == Clutter.Tab) {
let [completions, attrHead] = JsParse.getCompletions(text, commandHeader, AUTO_COMPLETE_GLOBAL_KEYWORDS);
let currTime = global.get_current_time();
if ((currTime - this._lastTabTime) < AUTO_COMPLETE_DOUBLE_TAB_DELAY) {
this._processCompletionRequest({ tabType: 'double',
completions: completions,
attrHead: attrHead });
} else {
this._processCompletionRequest({ tabType: 'single',
completions: completions,
attrHead: attrHead });
}
this._lastTabTime = currTime;
}
},
// Insert characters of text not already included in head at cursor position. i.e., if text="abc" and head="a",
// the string "bc" will be appended to this._entry
additionalCompletionText: function(text, head) {
let additionalCompletionText = text.slice(head.length);
let cursorPos = this._entry.clutter_text.get_cursor_position();
this._entry.clutter_text.insert_text(additionalCompletionText, cursorPos);
}
});
Signals.addSignalMethods(AutoComplete.prototype);
const Notebook = new Lang.Class({
Name: 'Notebook',
_init: function() {
this.actor = new St.BoxLayout({ vertical: true });
@ -149,25 +228,40 @@ Notebook.prototype = {
return;
let vAdjust = tabData.scrollView.vscroll.adjustment;
vAdjust.value = vAdjust.upper - vAdjust.page_size;
},
nextTab: function() {
let nextIndex = this._selectedIndex;
if (nextIndex < this._tabs.length - 1) {
++nextIndex;
}
this.selectIndex(nextIndex);
},
prevTab: function() {
let prevIndex = this._selectedIndex;
if (prevIndex > 0) {
--prevIndex;
}
this.selectIndex(prevIndex);
}
};
});
Signals.addSignalMethods(Notebook.prototype);
function objectToString(o) {
if (typeof(o) == typeof(objectToString)) {
// special case this since the default is way, way too verbose
return "<js function>";
return '<js function>';
} else {
return "" + o;
return '' + o;
}
}
function ObjLink(o, title) {
this._init(o, title);
}
ObjLink.prototype = {
__proto__: Link.Link,
const ObjLink = new Lang.Class({
Name: 'ObjLink',
Extends: Link.Link,
_init: function(o, title) {
let text;
@ -177,7 +271,8 @@ ObjLink.prototype = {
text = objectToString(o);
text = GLib.markup_escape_text(text, -1);
this._obj = o;
Link.Link.prototype._init.call(this, { label: text });
this.parent({ label: text });
this.actor.get_child().single_line_mode = true;
this.actor.connect('clicked', Lang.bind(this, this._onClicked));
},
@ -185,13 +280,11 @@ ObjLink.prototype = {
_onClicked: function (link) {
Main.lookingGlass.inspectObject(this._obj, this.actor);
}
};
});
function Result(command, o, index) {
this._init(command, o, index);
}
const Result = new Lang.Class({
Name: 'Result',
Result.prototype = {
_init : function(command, o, index) {
this.index = index;
this.o = o;
@ -213,19 +306,16 @@ Result.prototype = {
padBin.add_actor(line);
this.actor.add(padBin);
}
};
});
function WindowList() {
this._init();
}
const WindowList = new Lang.Class({
Name: 'WindowList',
WindowList.prototype = {
_init : function () {
this.actor = new St.BoxLayout({ name: 'Windows', vertical: true, style: 'spacing: 8px' });
let display = global.screen.get_display();
let tracker = Shell.WindowTracker.get_default();
this._updateId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._updateWindowList));
display.connect('window-created', Lang.bind(this, this._updateWindowList));
global.display.connect('window-created', Lang.bind(this, this._updateWindowList));
tracker.connect('tracked-windows-changed', Lang.bind(this, this._updateWindowList));
},
@ -248,7 +338,7 @@ WindowList.prototype = {
box.add(propsBox);
propsBox.add(new St.Label({ text: 'wmclass: ' + metaWindow.get_wm_class() }));
let app = tracker.get_window_app(metaWindow);
if (app != null && !app.is_transient()) {
if (app != null && !app.is_window_backed()) {
let icon = app.create_icon_texture(22);
let propBox = new St.BoxLayout({ style: 'spacing: 6px; ' });
propsBox.add(propBox);
@ -261,14 +351,12 @@ WindowList.prototype = {
}
}
}
};
});
Signals.addSignalMethods(WindowList.prototype);
function ObjInspector() {
this._init();
}
const ObjInspector = new Lang.Class({
Name: 'ObjInspector',
ObjInspector.prototype = {
_init : function () {
this._obj = null;
this._previousObj = null;
@ -322,7 +410,7 @@ ObjInspector.prototype = {
link = new St.Label({ text: '<error>' });
}
let hbox = new St.BoxLayout();
let propText = propName + ": " + valueStr;
let propText = propName + ': ' + valueStr;
hbox.add(new St.Label({ text: propName + ': ' }));
hbox.add(link);
this._container.add_actor(hbox);
@ -343,7 +431,7 @@ ObjInspector.prototype = {
this.actor.move_anchor_point(Math.floor(sourceX + sourceWidth / 2),
Math.floor(sourceY + sourceHeight / 2));
Tweener.addTween(this.actor, { scale_x: 1, scale_y: 1,
transition: "easeOutQuad",
transition: 'easeOutQuad',
time: 0.2 });
} else {
this.actor.set_scale(1, 1);
@ -368,7 +456,7 @@ ObjInspector.prototype = {
_onBack: function() {
this.selectObject(this._previousObj, true);
}
};
});
function addBorderPaintHook(actor) {
let signalId = actor.connect_after('paint',
@ -394,11 +482,9 @@ function addBorderPaintHook(actor) {
return signalId;
}
function Inspector() {
this._init();
}
const Inspector = new Lang.Class({
Name: 'Inspector',
Inspector.prototype = {
_init: function() {
let container = new Shell.GenericContainer({ width: 0,
height: 0 });
@ -537,15 +623,13 @@ Inspector.prototype = {
this._borderPaintId = addBorderPaintHook(this._target);
}
}
};
});
Signals.addSignalMethods(Inspector.prototype);
function ErrorLog() {
this._init();
}
const ErrorLog = new Lang.Class({
Name: 'ErrorLog',
ErrorLog.prototype = {
_init: function() {
this.actor = new St.BoxLayout();
this.text = new St.Label();
@ -580,13 +664,11 @@ ErrorLog.prototype = {
}
this.text.text = text;
}
};
});
function Memory() {
this._init();
}
const Memory = new Lang.Class({
Name: 'Memory',
Memory.prototype = {
_init: function() {
this.actor = new St.BoxLayout({ vertical: true });
this._glibc_uordblks = new St.Label();
@ -607,6 +689,9 @@ Memory.prototype = {
this._gjs_closure = new St.Label();
this.actor.add(this._gjs_closure);
this._last_gc_seconds_ago = new St.Label();
this.actor.add(this._last_gc_seconds_ago);
this._gcbutton = new St.Button({ label: 'Full GC',
style_class: 'lg-obj-inspector-button' });
this._gcbutton.connect('clicked', Lang.bind(this, function () { global.gc(); this._renderText(); }));
@ -626,107 +711,146 @@ Memory.prototype = {
this._gjs_gobject.text = 'gjs_gobject: ' + memInfo.gjs_gobject;
this._gjs_function.text = 'gjs_function: ' + memInfo.gjs_function;
this._gjs_closure.text = 'gjs_closure: ' + memInfo.gjs_closure;
this._last_gc_seconds_ago.text = 'last_gc_seconds_ago: ' + memInfo.last_gc_seconds_ago;
}
};
});
function Extensions() {
this._init();
}
const Extensions = new Lang.Class({
Name: 'Extensions',
Extensions.prototype = {
_init: function() {
this.actor = new St.BoxLayout({ vertical: true,
name: 'lookingGlassExtensions' });
this._noExtensions = new St.Label({ style_class: 'lg-extensions-none',
text: _("No extensions installed") });
this._numExtensions = 0;
this._extensionsList = new St.BoxLayout({ vertical: true,
style_class: 'lg-extensions-list' });
this._extensionsList.add(this._noExtensions);
this.actor.add(this._extensionsList);
this._loadExtensionList();
for (let uuid in ExtensionUtils.extensions)
this._loadExtension(null, uuid);
ExtensionSystem.connect('extension-loaded',
Lang.bind(this, this._loadExtension));
},
_loadExtensionList: function() {
let extensions = ExtensionSystem.extensionMeta;
let totalExtensions = 0;
for (let uuid in extensions) {
let extensionDisplay = this._createExtensionDisplay(extensions[uuid]);
this._extensionsList.add(extensionDisplay);
totalExtensions++;
}
if (totalExtensions == 0) {
this._extensionsList.add(this._noExtensions);
}
_loadExtension: function(o, uuid) {
let extension = ExtensionUtils.extensions[uuid];
// There can be cases where we create dummy extension metadata
// that's not really a proper extension. Don't bother with these.
if (!extension.metadata.name)
return;
let extensionDisplay = this._createExtensionDisplay(extension);
if (this._numExtensions == 0)
this._extensionsList.remove_actor(this._noExtensions);
this._numExtensions ++;
this._extensionsList.add(extensionDisplay);
},
_onViewSource: function (actor) {
let meta = actor._extensionMeta;
let file = Gio.file_new_for_path(meta.path);
let uri = file.get_uri();
let extension = actor._extension;
let uri = extension.dir.get_uri();
Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context());
Main.lookingGlass.close();
},
_onWebPage: function (actor) {
let meta = actor._extensionMeta;
Gio.app_info_launch_default_for_uri(meta.url, global.create_app_launch_context());
let extension = actor._extension;
Gio.app_info_launch_default_for_uri(extension.metadata.url, global.create_app_launch_context());
Main.lookingGlass.close();
},
_onViewErrors: function (actor) {
let extension = actor._extension;
let shouldShow = !actor._isShowing;
if (shouldShow) {
let errors = extension.errors;
let errorDisplay = new St.BoxLayout({ vertical: true });
if (errors && errors.length) {
for (let i = 0; i < errors.length; i ++)
errorDisplay.add(new St.Label({ text: errors[i] }));
} else {
/* Translators: argument is an extension UUID. */
let message = _("%s has not emitted any errors.").format(meta.uuid);
errorDisplay.add(new St.Label({ text: message }));
}
actor._errorDisplay = errorDisplay;
actor._parentBox.add(errorDisplay);
actor.label = _("Hide Errors");
} else {
actor._errorDisplay.destroy();
actor._errorDisplay = null;
actor.label = _("Show Errors");
}
actor._isShowing = shouldShow;
},
_stateToString: function(extensionState) {
switch (extensionState) {
case ExtensionSystem.ExtensionState.ENABLED:
return _("Enabled");
case ExtensionSystem.ExtensionState.DISABLED:
case ExtensionSystem.ExtensionState.INITIALIZED:
return _("Disabled");
case ExtensionSystem.ExtensionState.ERROR:
return _("Error");
case ExtensionSystem.ExtensionState.OUT_OF_DATE:
return _("Out of date");
case ExtensionSystem.ExtensionState.DOWNLOADING:
return _("Downloading");
}
return 'Unknown'; // Not translated, shouldn't appear
},
_createExtensionDisplay: function(meta) {
_createExtensionDisplay: function(extension) {
let box = new St.BoxLayout({ style_class: 'lg-extension', vertical: true });
let name = new St.Label({ style_class: 'lg-extension-name',
text: meta.name });
text: extension.metadata.name });
box.add(name, { expand: true });
let description = new St.Label({ style_class: 'lg-extension-description',
text: meta.description });
text: extension.metadata.description || 'No description' });
box.add(description, { expand: true });
let metaBox = new St.BoxLayout();
let metaBox = new St.BoxLayout({ style_class: 'lg-extension-meta' });
box.add(metaBox);
let stateString = this._stateToString(meta.state);
let stateString = this._stateToString(extension.state);
let state = new St.Label({ style_class: 'lg-extension-state',
text: this._stateToString(meta.state) });
let actionsContainer = new St.Bin({ x_align: St.Align.END });
metaBox.add(actionsContainer);
let actionsBox = new St.BoxLayout({ style_class: 'lg-extension-actions' });
actionsContainer.set_child(actionsBox);
text: this._stateToString(extension.state) });
metaBox.add(state);
let viewsource = new Link.Link({ label: _("View Source") });
viewsource.actor._extensionMeta = meta;
viewsource.actor._extension = extension;
viewsource.actor.connect('clicked', Lang.bind(this, this._onViewSource));
actionsBox.add(viewsource.actor);
metaBox.add(viewsource.actor);
if (meta.url) {
if (extension.metadata.url) {
let webpage = new Link.Link({ label: _("Web Page") });
webpage.actor._extensionMeta = meta;
webpage.actor._extension = extension;
webpage.actor.connect('clicked', Lang.bind(this, this._onWebPage));
actionsBox.add(webpage.actor);
metaBox.add(webpage.actor);
}
let viewerrors = new Link.Link({ label: _("Show Errors") });
viewerrors.actor._extension = extension;
viewerrors.actor._parentBox = box;
viewerrors.actor._isShowing = false;
viewerrors.actor.connect('clicked', Lang.bind(this, this._onViewErrors));
metaBox.add(viewerrors.actor);
return box;
}
};
});
function LookingGlass() {
this._init();
}
const LookingGlass = new Lang.Class({
Name: 'LookingGlass',
LookingGlass.prototype = {
_init : function() {
this._borderPaintTarget = null;
this._borderPaintId = 0;
@ -743,7 +867,8 @@ LookingGlass.prototype = {
this.actor = new St.BoxLayout({ name: 'LookingGlassDialog',
style_class: 'lg-dialog',
vertical: true,
visible: false });
visible: false,
reactive: true });
this.actor.connect('key-press-event', Lang.bind(this, this._globalKeyPressEvent));
this._interfaceSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
@ -751,7 +876,13 @@ LookingGlass.prototype = {
Lang.bind(this, this._updateFont));
this._updateFont();
Main.uiGroup.add_actor(this.actor);
// We want it to appear to slide out from underneath the panel
Main.layoutManager.panelBox.add_actor(this.actor);
this.actor.lower_bottom();
Main.layoutManager.panelBox.connect('allocation-changed',
Lang.bind(this, this._queueResize));
Main.layoutManager.keyboardBox.connect('allocation-changed',
Lang.bind(this, this._queueResize));
this._objInspector = new ObjInspector();
Main.uiGroup.add_actor(this._objInspector.actor);
@ -792,14 +923,15 @@ LookingGlass.prototype = {
this._resultsArea = new St.BoxLayout({ name: 'ResultsArea', vertical: true });
this._evalBox.add(this._resultsArea, { expand: true });
let entryArea = new St.BoxLayout({ name: 'EntryArea' });
this._evalBox.add_actor(entryArea);
this._entryArea = new St.BoxLayout({ name: 'EntryArea' });
this._evalBox.add_actor(this._entryArea);
let label = new St.Label({ text: 'js>>> ' });
entryArea.add(label);
this._entryArea.add(label);
this._entry = new St.Entry({ can_focus: true });
entryArea.add(this._entry, { expand: true });
ShellEntry.addContextMenu(this._entry);
this._entryArea.add(this._entry, { expand: true });
this._windowList = new WindowList();
this._windowList.connect('selected', Lang.bind(this, function(list, window) {
@ -818,6 +950,9 @@ LookingGlass.prototype = {
notebook.appendPage('Extensions', this._extensions.actor);
this._entry.clutter_text.connect('activate', Lang.bind(this, function (o, e) {
// Hide any completions we are currently showing
this._hideCompletions();
let text = o.get_text();
// Ensure we don't get newlines in the command; the history file is
// newline-separated.
@ -832,6 +967,19 @@ LookingGlass.prototype = {
this._history = new History.HistoryManager({ gsettingsKey: HISTORY_KEY,
entry: this._entry.clutter_text });
this._autoComplete = new AutoComplete(this._entry);
this._autoComplete.connect('suggest', Lang.bind(this, function(a,e) {
this._showCompletions(e.completions);
}));
// If a completion is completed unambiguously, the currently-displayed completion
// suggestions become irrelevant.
this._autoComplete.connect('completion', Lang.bind(this, function(a,e) {
if (e.type == 'whole-word')
this._hideCompletions();
}));
this._resize();
},
_updateFont: function() {
@ -875,6 +1023,59 @@ LookingGlass.prototype = {
this._notebook.scrollToBottom(0);
},
_showCompletions: function(completions) {
if (!this._completionActor) {
let actor = new St.BoxLayout({ vertical: true });
this._completionText = new St.Label({ name: 'LookingGlassAutoCompletionText', style_class: 'lg-completions-text' });
this._completionText.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this._completionText.clutter_text.line_wrap = true;
actor.add(this._completionText);
let line = new Clutter.Rectangle();
let padBin = new St.Bin({ x_fill: true, y_fill: true });
padBin.add_actor(line);
actor.add(padBin);
this._completionActor = actor;
this._evalBox.insert_before(this._completionActor, this._entryArea);
}
this._completionText.set_text(completions.join(', '));
// Setting the height to -1 allows us to get its actual preferred height rather than
// whatever was last given in set_height by Tweener.
this._completionActor.set_height(-1);
let [minHeight, naturalHeight] = this._completionText.get_preferred_height(this._resultsArea.get_width());
// Don't reanimate if we are already visible
if (this._completionActor.visible) {
this._completionActor.height = naturalHeight;
} else {
this._completionActor.show();
Tweener.removeTweens(this._completionActor);
Tweener.addTween(this._completionActor, { time: AUTO_COMPLETE_SHOW_COMPLETION_ANIMATION_DURATION / St.get_slow_down_factor(),
transition: 'easeOutQuad',
height: naturalHeight,
opacity: 255
});
}
},
_hideCompletions: function() {
if (this._completionActor) {
Tweener.removeTweens(this._completionActor);
Tweener.addTween(this._completionActor, { time: AUTO_COMPLETE_SHOW_COMPLETION_ANIMATION_DURATION / St.get_slow_down_factor(),
transition: 'easeOutQuad',
height: 0,
opacity: 0,
onComplete: Lang.bind(this, function () {
this._completionActor.hide();
})
});
}
},
_evaluate : function(command) {
this._history.addItem(command);
@ -906,13 +1107,18 @@ LookingGlass.prototype = {
this.open();
},
_resizeTo: function(actor) {
_queueResize: function() {
Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
Lang.bind(this, function () { this._resize(); }));
},
_resize: function() {
let primary = Main.layoutManager.primaryMonitor;
let myWidth = primary.width * 0.7;
let myHeight = primary.height * 0.7;
let [srcX, srcY] = actor.get_transformed_position();
this.actor.x = srcX + (primary.width - myWidth) / 2;
this._hiddenY = srcY + actor.height - myHeight - 4; // -4 to hide the top corners
let availableHeight = primary.height - Main.layoutManager.keyboardBox.height;
let myHeight = Math.min(primary.height * 0.7, availableHeight * 0.9);
this.actor.x = (primary.width - myWidth) / 2;
this._hiddenY = this.actor.get_parent().height - myHeight - 4; // -4 to hide the top corners
this._targetY = this._hiddenY + myHeight;
this.actor.y = this._hiddenY;
this.actor.width = myWidth;
@ -922,14 +1128,6 @@ LookingGlass.prototype = {
this._targetY + Math.floor(myHeight * 0.1));
},
slaveTo: function(actor) {
this._slaveTo = actor;
actor.connect('notify::allocation', Lang.bind(this, function () {
this._resizeTo(actor);
}));
this._resizeTo(actor);
},
insertObject: function(obj) {
this._pushResult('<insert>', obj);
},
@ -942,6 +1140,7 @@ LookingGlass.prototype = {
// Handle key events which are relevant for all tabs of the LookingGlass
_globalKeyPressEvent : function(actor, event) {
let symbol = event.get_key_symbol();
let modifierState = Shell.get_event_state(event);
if (symbol == Clutter.Escape) {
if (this._objInspector.actor.visible) {
this._objInspector.close();
@ -950,6 +1149,14 @@ LookingGlass.prototype = {
}
return true;
}
// Ctrl+PgUp and Ctrl+PgDown switches tabs in the notebook view
if (modifierState & Clutter.ModifierType.CONTROL_MASK) {
if (symbol == Clutter.KEY_Page_Up) {
this._notebook.prevTab();
} else if (symbol == Clutter.KEY_Page_Down) {
this._notebook.nextTab();
}
}
return false;
},
@ -962,7 +1169,6 @@ LookingGlass.prototype = {
this._notebook.selectIndex(0);
this.actor.show();
this.actor.lower(Main.chrome.actor);
this._open = true;
this._history.lastItem();
@ -1001,5 +1207,5 @@ LookingGlass.prototype = {
})
});
}
};
});
Signals.addSignalMethods(LookingGlass.prototype);

View File

@ -1,6 +1,7 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GDesktopEnums = imports.gi.GDesktopEnums;
const Gio = imports.gi.Gio;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
@ -12,22 +13,6 @@ const Main = imports.ui.main;
const MagnifierDBus = imports.ui.magnifierDBus;
const Params = imports.misc.params;
// Keep enums in sync with GSettings schemas
const MouseTrackingMode = {
NONE: 0,
CENTERED: 1,
PROPORTIONAL: 2,
PUSH: 3
};
const ScreenPosition = {
NONE: 0,
FULL_SCREEN: 1,
TOP_HALF: 2,
BOTTOM_HALF: 3,
LEFT_HALF: 4,
RIGHT_HALF: 5
};
const MOUSE_POLL_FREQUENCY = 50;
const CROSSHAIRS_CLIP_SIZE = [100, 100];
@ -51,17 +36,15 @@ const CROSS_HAIRS_CLIP_KEY = 'cross-hairs-clip';
let magDBusService = null;
function Magnifier() {
this._init();
}
const Magnifier = new Lang.Class({
Name: 'Magnifier',
Magnifier.prototype = {
_init: function() {
// Magnifier is a manager of ZoomRegions.
this._zoomRegions = [];
// Create small clutter tree for the magnified mouse.
let xfixesCursor = Shell.XFixesCursor.get_default();
let xfixesCursor = Shell.XFixesCursor.get_for_stage(global.stage);
this._mouseSprite = new Clutter.Texture();
xfixesCursor.update_texture_image(this._mouseSprite);
this._cursorRoot = new Clutter.Group();
@ -520,7 +503,7 @@ Magnifier.prototype = {
if (this._zoomRegions.length) {
let position = this._settings.get_enum(SCREEN_POSITION_KEY);
this._zoomRegions[0].setScreenPosition(position);
if (position != ScreenPosition.FULL_SCREEN)
if (position != GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN)
this._updateLensMode();
}
},
@ -558,23 +541,22 @@ Magnifier.prototype = {
);
}
}
};
});
Signals.addSignalMethods(Magnifier.prototype);
function ZoomRegion(magnifier, mouseSourceActor) {
this._init(magnifier, mouseSourceActor);
}
const ZoomRegion = new Lang.Class({
Name: 'ZoomRegion',
ZoomRegion.prototype = {
_init: function(magnifier, mouseSourceActor) {
this._magnifier = magnifier;
this._mouseTrackingMode = MouseTrackingMode.NONE;
this._mouseTrackingMode = GDesktopEnums.MagnifierMouseTrackingMode.NONE;
this._clampScrollingAtEdges = false;
this._lensMode = false;
this._screenPosition = ScreenPosition.FULL_SCREEN;
this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN;
this._magView = null;
this._background = null;
this._uiGroupClone = null;
this._mouseSourceActor = mouseSourceActor;
this._mouseActor = null;
@ -584,12 +566,15 @@ ZoomRegion.prototype = {
this._viewPortX = 0;
this._viewPortY = 0;
this._viewPortWidth = global.screen_width;
this._viewPortWidth = global.screen_height;
this._viewPortHeight = global.screen_height;
this._xCenter = this._viewPortWidth / 2;
this._yCenter = this._viewPortHeight / 2;
this._xMagFactor = 1;
this._yMagFactor = 1;
this._followingCursor = false;
Main.layoutManager.connect('monitors-changed',
Lang.bind(this, this._monitorsChanged));
},
/**
@ -647,7 +632,8 @@ ZoomRegion.prototype = {
* @mode: One of the enum MouseTrackingMode values.
*/
setMouseTrackingMode: function(mode) {
if (mode >= MouseTrackingMode.NONE && mode <= MouseTrackingMode.PUSH)
if (mode >= GDesktopEnums.MagnifierMouseTrackingMode.NONE &&
mode <= GDesktopEnums.MagnifierMouseTrackingMode.PUSH)
this._mouseTrackingMode = mode;
},
@ -668,7 +654,7 @@ ZoomRegion.prototype = {
*/
setViewPort: function(viewPort) {
this._setViewPort(viewPort);
this._screenPosition = ScreenPosition.NONE;
this._screenPosition = GDesktopEnums.MagnifierScreenPosition.NONE;
},
/**
@ -750,7 +736,7 @@ ZoomRegion.prototype = {
viewPort.width = global.screen_width;
viewPort.height = global.screen_height/2;
this._setViewPort(viewPort);
this._screenPosition = ScreenPosition.TOP_HALF;
this._screenPosition = GDesktopEnums.MagnifierScreenPosition.TOP_HALF;
},
/**
@ -764,7 +750,7 @@ ZoomRegion.prototype = {
viewPort.width = global.screen_width;
viewPort.height = global.screen_height/2;
this._setViewPort(viewPort);
this._screenPosition = ScreenPosition.BOTTOM_HALF;
this._screenPosition = GDesktopEnums.MagnifierScreenPosition.BOTTOM_HALF;
},
/**
@ -778,7 +764,7 @@ ZoomRegion.prototype = {
viewPort.width = global.screen_width/2;
viewPort.height = global.screen_height;
this._setViewPort(viewPort);
this._screenPosition = ScreenPosition.LEFT_HALF;
this._screenPosition = GDesktopEnums.MagnifierScreenPosition.LEFT_HALF;
},
/**
@ -792,7 +778,7 @@ ZoomRegion.prototype = {
viewPort.width = global.screen_width/2;
viewPort.height = global.screen_height;
this._setViewPort(viewPort);
this._screenPosition = ScreenPosition.RIGHT_HALF;
this._screenPosition = GDesktopEnums.MagnifierScreenPosition.RIGHT_HALF;
},
/**
@ -808,7 +794,7 @@ ZoomRegion.prototype = {
viewPort.height = global.screen_height;
this.setViewPort(viewPort);
this._screenPosition = ScreenPosition.FULL_SCREEN;
this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN;
},
/**
@ -821,19 +807,19 @@ ZoomRegion.prototype = {
*/
setScreenPosition: function(inPosition) {
switch (inPosition) {
case ScreenPosition.FULL_SCREEN:
case GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN:
this.setFullScreenMode();
break;
case ScreenPosition.TOP_HALF:
case GDesktopEnums.MagnifierScreenPosition.TOP_HALF:
this.setTopHalf();
break;
case ScreenPosition.BOTTOM_HALF:
case GDesktopEnums.MagnifierScreenPosition.BOTTOM_HALF:
this.setBottomHalf();
break;
case ScreenPosition.LEFT_HALF:
case GDesktopEnums.MagnifierScreenPosition.LEFT_HALF:
this.setLeftHalf();
break;
case ScreenPosition.RIGHT_HALF:
case GDesktopEnums.MagnifierScreenPosition.RIGHT_HALF:
this.setRightHalf();
break;
}
@ -856,7 +842,7 @@ ZoomRegion.prototype = {
*/
scrollToMousePos: function() {
this._followingCursor = true;
if (this._mouseTrackingMode != MouseTrackingMode.NONE)
if (this._mouseTrackingMode != GDesktopEnums.MagnifierMouseTrackingMode.NONE)
this._changeROI({ redoCursorTracking: true });
else
this._updateMousePosition();
@ -909,15 +895,15 @@ ZoomRegion.prototype = {
// Add a background for when the magnified uiGroup is scrolled
// out of view (don't want to see desktop showing through).
let background = new Clutter.Rectangle({ color: Main.DEFAULT_BACKGROUND_COLOR });
mainGroup.add_actor(background);
this._background = new Clutter.Rectangle({ color: Main.DEFAULT_BACKGROUND_COLOR });
mainGroup.add_actor(this._background);
// Clone the group that contains all of UI on the screen. This is the
// chrome, the windows, etc.
this._uiGroupClone = new Clutter.Clone({ source: Main.uiGroup });
mainGroup.add_actor(this._uiGroupClone);
Main.uiGroup.set_size(global.screen_width, global.screen_height);
background.set_size(global.screen_width, global.screen_height);
this._background.set_size(global.screen_width, global.screen_height);
// Add either the given mouseSourceActor to the ZoomRegion, or a clone of
// it.
@ -941,6 +927,7 @@ ZoomRegion.prototype = {
this._magView.destroy();
this._magView = null;
this._background = null;
this._uiGroupClone = null;
this._mouseActor = null;
this._crossHairsActor = null;
@ -991,7 +978,7 @@ ZoomRegion.prototype = {
this._yMagFactor = params.yMagFactor;
if (params.redoCursorTracking &&
this._mouseTrackingMode != MouseTrackingMode.NONE) {
this._mouseTrackingMode != GDesktopEnums.MagnifierMouseTrackingMode.NONE) {
// This depends on this.xMagFactor/yMagFactor already being updated
[params.xCenter, params.yCenter] = this._centerFromMousePosition();
}
@ -1041,7 +1028,7 @@ ZoomRegion.prototype = {
_isFullScreen: function() {
// Does the magnified view occupy the whole screen? Note that this
// doesn't necessarily imply
// this._screenPosition = ScreenPosition.FULL_SCREEN;
// this._screenPosition = GDesktopEnums.MagnifierScreenPosition.FULL_SCREEN;
if (this._viewPortX != 0 || this._viewPortY != 0)
return false;
@ -1058,13 +1045,13 @@ ZoomRegion.prototype = {
let xMouse = this._magnifier.xMouse;
let yMouse = this._magnifier.yMouse;
if (this._mouseTrackingMode == MouseTrackingMode.PROPORTIONAL) {
if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PROPORTIONAL) {
return this._centerFromMouseProportional(xMouse, yMouse);
}
else if (this._mouseTrackingMode == MouseTrackingMode.PUSH) {
else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.PUSH) {
return this._centerFromMousePush(xMouse, yMouse);
}
else if (this._mouseTrackingMode == MouseTrackingMode.CENTERED) {
else if (this._mouseTrackingMode == GDesktopEnums.MagnifierMouseTrackingMode.CENTERED) {
return this._centerFromMouseCentered(xMouse, yMouse);
}
@ -1163,14 +1150,28 @@ ZoomRegion.prototype = {
this._crossHairsActor.set_position(xMagMouse - groupWidth / 2,
yMagMouse - groupHeight / 2);
}
},
_monitorsChanged: function() {
if (!this.isActive())
return;
Main.uiGroup.set_size(global.screen_width, global.screen_height);
this._background.set_size(global.screen_width, global.screen_height);
if (this._screenPosition == GDesktopEnums.MagnifierScreenPosition.NONE)
this._setViewPort({ x: this._viewPortX,
y: this._viewPortY,
width: this._viewPortWidth,
height: this._viewPortHeight });
else
this.setScreenPosition(this._screenPosition);
}
};
});
function Crosshairs() {
this._init();
}
const Crosshairs = new Lang.Class({
Name: 'Crosshairs',
Crosshairs.prototype = {
_init: function() {
// Set the group containing the crosshairs to three times the desktop
@ -1195,6 +1196,14 @@ Crosshairs.prototype = {
this._clipSize = [0, 0];
this._clones = [];
this.reCenter();
Main.layoutManager.connect('monitors-changed',
Lang.bind(this, this._monitorsChanged));
},
_monitorsChanged: function() {
this._actor.set_size(global.screen_width * 3, global.screen_height * 3);
this.reCenter();
},
/**
@ -1426,4 +1435,4 @@ Crosshairs.prototype = {
this._vertTopHair.set_position((groupWidth - thickness) / 2, top);
this._vertBottomHair.set_position((groupWidth - thickness) / 2, bottom);
}
};
});

View File

@ -1,6 +1,7 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const DBus = imports.dbus;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const Main = imports.ui.main;
const MAG_SERVICE_NAME = 'org.gnome.Magnifier';
@ -10,61 +11,99 @@ const ZOOM_SERVICE_PATH = '/org/gnome/Magnifier/ZoomRegion';
// Subset of gnome-mag's Magnifier dbus interface -- to be expanded. See:
// http://git.gnome.org/browse/gnome-mag/tree/xml/...Magnifier.xml
const MagnifierIface = {
name: MAG_SERVICE_NAME,
methods: [
{ name: 'setActive', inSignature: 'b', outSignature: '' },
{ name: 'isActive', inSignature: '', outSignature: 'b' },
{ name: 'showCursor', inSignature: '', outSignature: '' },
{ name: 'hideCursor', inSignature: '', outSignature: '' },
{ name: 'createZoomRegion', inSignature: 'ddaiai', outSignature: 'o' },
{ name: 'addZoomRegion', inSignature: 'o', outSignature: 'b' },
{ name: 'getZoomRegions', inSignature: '', outSignature: 'ao' },
{ name: 'clearAllZoomRegions', inSignature: '', outSignature: '' },
{ name: 'fullScreenCapable', inSignature: '', outSignature: 'b' },
{ name: 'setCrosswireSize', inSignature: 'i', outSignature: '' },
{ name: 'getCrosswireSize', inSignature: '', outSignature: 'i' },
{ name: 'setCrosswireLength', inSignature: 'i', outSignature: '' },
{ name: 'getCrosswireLength', inSignature: '', outSignature: 'i' },
{ name: 'setCrosswireClip', inSignature: 'b', outSignature: '' },
{ name: 'getCrosswireClip', inSignature: '', outSignature: 'b' },
{ name: 'setCrosswireColor', inSignature: 'u', outSignature: '' },
{ name: 'getCrosswireColor', inSignature: '', outSignature: 'u' }
],
signals: [],
properties: []
};
const MagnifierIface = <interface name={MAG_SERVICE_NAME}>
<method name="setActive">
<arg type="b" direction="in" />
</method>
<method name="isActive">
<arg type="b" direction="out" />
</method>
<method name="showCursor" />
<method name="hideCursor" />
<method name="createZoomRegion">
<arg type="d" direction="in" />
<arg type="d" direction="in" />
<arg type="ai" direction="in" />
<arg type="ai" direction="in" />
<arg type="o" direction="out" />
</method>
<method name="addZoomRegion">
<arg type="o" direction="in" />
<arg type="b" direction="out" />
</method>
<method name="getZoomRegions">
<arg type="ao" direction="out" />
</method>
<method name="clearAllZoomRegions" />
<method name="fullScreenCapable">
<arg type="b" direction="out" />
</method>
<method name="setCrosswireSize">
<arg type="i" direction="in" />
</method>
<method name="getCrosswireSize">
<arg type="i" direction="out" />
</method>
<method name="setCrosswireLength">
<arg type="i" direction="in" />
</method>
<method name="getCrosswireLength">
<arg type="i" direction="out" />
</method>
<method name="setCrosswireClip">
<arg type="b" direction="in" />
</method>
<method name="getCrosswireClip">
<arg type="b" direction="out" />
</method>
<method name="setCrosswireColor">
<arg type="u" direction="in" />
</method>
<method name="getCrosswireColor">
<arg type="u" direction="out" />
</method>
</interface>;
// Subset of gnome-mag's ZoomRegion dbus interface -- to be expanded. See:
// http://git.gnome.org/browse/gnome-mag/tree/xml/...ZoomRegion.xml
const ZoomRegionIface = {
name: ZOOM_SERVICE_NAME,
methods: [
{ name: 'setMagFactor', inSignature: 'dd', outSignature: ''},
{ name: 'getMagFactor', inSignature: '', outSignature: 'dd' },
{ name: 'setRoi', inSignature: 'ai', outSignature: '' },
{ name: 'getRoi', inSignature: '', outSignature: 'ai' },
{ name: 'shiftContentsTo', inSignature: 'ii', outSignature: 'b' },
{ name: 'moveResize', inSignature: 'ai', outSignature: '' }
],
signals: [],
properties: []
};
const ZoomRegionIface = <interface name={ZOOM_SERVICE_NAME}>
<method name="setMagFactor">
<arg type="d" direction="in" />
<arg type="d" direction="in" />
</method>
<method name="getMagFactor">
<arg type="d" direction="out" />
<arg type="d" direction="out" />
</method>
<method name="setRoi">
<arg type="ai" direction="in" />
</method>
<method name="getRoi">
<arg type="ai" direction="out" />
</method>
<method name="shiftContentsTo">
<arg type="i" direction="in" />
<arg type="i" direction="in" />
<arg type="b" direction="out" />
</method>
<method name="moveResize">
<arg type="ai" direction="in" />
</method>
</interface>;
// For making unique ZoomRegion DBus proxy object paths of the form:
// '/org/gnome/Magnifier/ZoomRegion/zoomer0',
// '/org/gnome/Magnifier/ZoomRegion/zoomer1', etc.
let _zoomRegionInstanceCount = 0;
function ShellMagnifier() {
this._init();
}
const ShellMagnifier = new Lang.Class({
Name: 'ShellMagnifier',
ShellMagnifier.prototype = {
_init: function() {
this._zoomers = {};
DBus.session.exportObject(MAG_SERVICE_PATH, this);
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(MagnifierIface, this);
this._dbusImpl.export(Gio.DBus.session, MAG_SERVICE_PATH);
},
/**
@ -195,10 +234,10 @@ ShellMagnifier.prototype = {
Main.magnifier.clearAllZoomRegions();
for (let objectPath in this._zoomers) {
let proxyAndZoomer = this._zoomers[objectPath];
proxyAndZoomer.proxy.destroy();
proxyAndZoomer.proxy = null;
proxyAndZoomer.zoomRegion = null;
delete this._zoomers[objectPath];
DBus.session.unexportObject(proxyAndZoomer);
}
this._zoomers = {};
},
@ -285,7 +324,7 @@ ShellMagnifier.prototype = {
// Drop the leading '#'.
return parseInt(colorString.slice(1), 16);
}
};
});
/**
* ShellMagnifierZoomRegion:
@ -293,15 +332,14 @@ ShellMagnifier.prototype = {
* @zoomerObjectPath: String that is the path to a DBus ZoomRegion.
* @zoomRegion: The actual zoom region associated with the object path.
*/
function ShellMagnifierZoomRegion(zoomerObjectPath, zoomRegion) {
this._init(zoomerObjectPath, zoomRegion);
}
const ShellMagnifierZoomRegion = new Lang.Class({
Name: 'ShellMagnifierZoomRegion',
ShellMagnifierZoomRegion.prototype = {
_init: function(zoomerObjectPath, zoomRegion) {
this._zoomRegion = zoomRegion;
DBus.session.proxifyObject(this, ZOOM_SERVICE_NAME, zoomerObjectPath);
DBus.session.exportObject(zoomerObjectPath, this);
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ZoomRegionIface, this);
this._dbusImpl.export(Gio.DBus.session, zoomerObjectPath);
},
/**
@ -376,8 +414,9 @@ ShellMagnifierZoomRegion.prototype = {
moveResize: function(viewPort) {
let viewRect = { x: viewPort[0], y: viewPort[1], width: viewPort[2] - viewPort[0], height: viewPort[3] - viewPort[1] };
this._zoomRegion.setViewPort(viewRect);
}
};
},
DBus.conformExport(ShellMagnifier.prototype, MagnifierIface);
DBus.conformExport(ShellMagnifierZoomRegion.prototype, ZoomRegionIface);
destroy: function() {
this._dbusImpl.unexport();
}
});

View File

@ -1,11 +1,9 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const DBus = imports.dbus;
const Gdk = imports.gi.Gdk;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GConf = imports.gi.GConf;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@ -14,12 +12,12 @@ const St = imports.gi.St;
const AutomountManager = imports.ui.automountManager;
const AutorunManager = imports.ui.autorunManager;
const Chrome = imports.ui.chrome;
const CtrlAltTab = imports.ui.ctrlAltTab;
const EndSessionDialog = imports.ui.endSessionDialog;
const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
const Environment = imports.ui.environment;
const ExtensionSystem = imports.ui.extensionSystem;
const Keyboard = imports.ui.keyboard;
const MessageTray = imports.ui.messageTray;
const Overview = imports.ui.overview;
const Panel = imports.ui.panel;
@ -27,6 +25,7 @@ const PlaceDisplay = imports.ui.placeDisplay;
const RunDialog = imports.ui.runDialog;
const Layout = imports.ui.layout;
const LookingGlass = imports.ui.lookingGlass;
const NetworkAgent = imports.ui.networkAgent;
const NotificationDaemon = imports.ui.notificationDaemon;
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
const Scripting = imports.ui.scripting;
@ -43,7 +42,6 @@ DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
let automountManager = null;
let autorunManager = null;
let chrome = null;
let panel = null;
let hotCorners = [];
let placesManager = null;
@ -64,14 +62,88 @@ let uiGroup = null;
let magnifier = null;
let xdndHandler = null;
let statusIconDispatcher = null;
let keyboard = null;
let layoutManager = null;
let networkAgent = null;
let _errorLogStack = [];
let _startDate;
let _defaultCssStylesheet = null;
let _cssStylesheet = null;
let _gdmCssStylesheet = null;
let background = null;
function _createUserSession() {
// Load the calendar server. Note that we are careful about
// not loading any events until the user presses the clock
global.launch_calendar_server();
placesManager = new PlaceDisplay.PlacesManager();
telepathyClient = new TelepathyClient.Client();
automountManager = new AutomountManager.AutomountManager();
autorunManager = new AutorunManager.AutorunManager();
networkAgent = new NetworkAgent.NetworkAgent();
}
function _createGDMSession() {
// We do this this here instead of at the top to prevent GDM
// related code from getting loaded in normal user sessions
const LoginDialog = imports.gdm.loginDialog;
let loginDialog = new LoginDialog.LoginDialog();
loginDialog.connect('loaded', function() {
loginDialog.open();
});
}
function _initRecorder() {
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
global.screen.connect('toggle-recording', function() {
if (recorder == null) {
recorder = new Shell.Recorder({ stage: global.stage });
}
if (recorder.is_recording()) {
recorder.pause();
Meta.enable_unredirect_for_screen(global.screen);
} else {
// read the parameters from GSettings always in case they have changed
recorder.set_framerate(recorderSettings.get_int('framerate'));
recorder.set_filename('shell-%d%u-%c.' + recorderSettings.get_string('file-extension'));
let pipeline = recorderSettings.get_string('pipeline');
if (!pipeline.match(/^\s*$/))
recorder.set_pipeline(pipeline);
else
recorder.set_pipeline(null);
Meta.disable_unredirect_for_screen(global.screen);
recorder.record();
}
});
}
function _initUserSession() {
_initRecorder();
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
ExtensionSystem.init();
ExtensionSystem.loadExtensions();
Meta.keybindings_set_custom_handler('panel-run-dialog', function() {
getRunDialog().open();
});
Meta.keybindings_set_custom_handler('panel-main-menu', function () {
overview.toggle();
});
global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
}
function start() {
// Monkey patch utility functions into the global proxy;
// This is easier and faster than indirecting down into global
@ -85,15 +157,6 @@ function start() {
Gio.DesktopAppInfo.set_desktop_env('GNOME');
shellDBusService = new ShellDBus.GnomeShell();
// Force a connection now; dbus.js will do this internally
// if we use its name acquisition stuff but we aren't right
// now; to do so we'd need to convert from its async calls
// back into sync ones.
DBus.session.flush();
// Load the calendar server. Note that we are careful about
// not loading any events until the user presses the clock
global.launch_calendar_server();
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
// also initialize ShellAppSystem first. ShellAppSystem
@ -113,74 +176,51 @@ function start() {
global.stage.no_clear_hint = true;
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
_gdmCssStylesheet = global.datadir + '/theme/gdm.css';
loadTheme();
let shellwm = global.window_manager;
shellwm.takeover_keybinding('panel_main_menu');
shellwm.connect('keybinding::panel_main_menu', function () {
overview.toggle();
});
shellwm.takeover_keybinding('panel_run_dialog');
shellwm.connect('keybinding::panel_run_dialog', function () {
getRunDialog().open();
});
// Set up stage hierarchy to group all UI actors under one container.
uiGroup = new Clutter.Group();
uiGroup = new Shell.GenericContainer({ name: 'uiGroup' });
uiGroup.connect('allocate',
function (actor, box, flags) {
let children = uiGroup.get_children();
for (let i = 0; i < children.length; i++)
children[i].allocate_preferred_size(flags);
});
St.set_ui_root(global.stage, uiGroup);
global.window_group.reparent(uiGroup);
global.overlay_group.reparent(uiGroup);
global.stage.add_actor(uiGroup);
layoutManager = new Layout.LayoutManager();
placesManager = new PlaceDisplay.PlacesManager();
xdndHandler = new XdndHandler.XdndHandler();
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
overview = new Overview.Overview();
chrome = new Chrome.Chrome();
// This overview object is just a stub for non-user sessions
overview = new Overview.Overview({ isDummy: global.session_type != Shell.SessionType.USER });
magnifier = new Magnifier.Magnifier();
statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher();
panel = new Panel.Panel();
wm = new WindowManager.WindowManager();
messageTray = new MessageTray.MessageTray();
keyboard = new Keyboard.Keyboard();
notificationDaemon = new NotificationDaemon.NotificationDaemon();
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
telepathyClient = new TelepathyClient.Client();
automountManager = new AutomountManager.AutomountManager();
autorunManager = new AutorunManager.AutorunManager();
if (global.session_type == Shell.SessionType.USER)
_createUserSession();
else if (global.session_type == Shell.SessionType.GDM)
_createGDMSession();
panel.startStatusArea();
layoutManager.init();
keyboard.init();
overview.init();
if (global.session_type == Shell.SessionType.USER)
_initUserSession();
statusIconDispatcher.start(messageTray.actor);
_startDate = new Date();
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
global.screen.connect('toggle-recording', function() {
if (recorder == null) {
recorder = new Shell.Recorder({ stage: global.stage });
}
if (recorder.is_recording()) {
recorder.pause();
} else {
// read the parameters from GSettings always in case they have changed
recorder.set_framerate(recorderSettings.get_int('framerate'));
recorder.set_filename('shell-%d%u-%c.' + recorderSettings.get_string('file-extension'));
let pipeline = recorderSettings.get_string('pipeline');
if (!pipeline.match(/^\s*$/))
recorder.set_pipeline(pipeline);
else
recorder.set_pipeline(null);
recorder.record();
}
});
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
// Provide the bus object for gnome-session to
// initiate logouts.
EndSessionDialog.init();
@ -188,14 +228,7 @@ function start() {
// Attempt to become a PolicyKit authentication agent
PolkitAuthenticationAgent.init()
ExtensionSystem.init();
ExtensionSystem.loadExtensions();
panel.startStatusArea();
panel.startupAnimation();
let display = global.screen.get_display();
display.connect('overlay-key', Lang.bind(overview, overview.toggle));
_startDate = new Date();
global.stage.connect('captured-event', _globalKeyPressHandler);
@ -299,6 +332,7 @@ function _windowRemoved(workspace, window) {
workspace._lastRemovedWindow = null;
_queueCheckWorkspaces();
}
return false;
});
}
@ -414,6 +448,9 @@ function loadTheme() {
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
if (global.session_type == Shell.SessionType.GDM)
theme.load_stylesheet(_gdmCssStylesheet);
if (previousTheme) {
let customStylesheets = previousTheme.get_custom_stylesheets();
@ -424,6 +461,19 @@ function loadTheme() {
themeContext.set_theme (theme);
}
/**
* notify:
* @msg: A message
* @details: Additional information
*/
function notify(msg, details) {
let source = new MessageTray.SystemNotificationSource();
messageTray.add(source);
let notification = new MessageTray.Notification(source, msg, details);
notification.setTransient(true);
source.notify(notification);
}
/**
* notifyError:
* @msg: An error message
@ -434,15 +484,11 @@ function loadTheme() {
function notifyError(msg, details) {
// Also print to stderr so it's logged somewhere
if (details)
log("error: " + msg + ": " + details);
log('error: ' + msg + ': ' + details);
else
log("error: " + msg)
log('error: ' + msg);
let source = new MessageTray.SystemNotificationSource();
messageTray.add(source);
let notification = new MessageTray.Notification(source, msg, details);
notification.setTransient(true);
source.notify(notification);
notify(msg, details);
}
/**
@ -524,23 +570,12 @@ function _globalKeyPressHandler(actor, event) {
let keyCode = event.get_key_code();
let modifierState = Shell.get_event_state(event);
let display = global.screen.get_display();
// This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
let action = display.get_keybinding_action(keyCode, modifierState);
let action = global.display.get_keybinding_action(keyCode, modifierState);
// The screenshot action should always be available (even if a
// modal dialog is present)
if (action == Meta.KeyBindingAction.COMMAND_SCREENSHOT) {
let gconf = GConf.Client.get_default();
let command = gconf.get_string('/apps/metacity/keybinding_commands/command_screenshot');
if (command != null && command != '')
Util.spawnCommandLine(command);
return true;
}
// Other bindings are only available when the overview is up and
// Other bindings are only available to the user session when the overview is up and
// no modal dialog is present.
if (!overview.visible || modalCount > 1)
if (global.session_type == Shell.SessionType.USER && (!overview.visible || modalCount > 1))
return false;
// This isn't a Meta.KeyBindingAction yet
@ -549,6 +584,16 @@ function _globalKeyPressHandler(actor, event) {
return true;
}
if (action == Meta.KeyBindingAction.SWITCH_PANELS) {
ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK,
modifierState);
return true;
}
// None of the other bindings are relevant outside of the user's session
if (global.session_type != Shell.SessionType.USER)
return false;
switch (action) {
// left/right would effectively act as synonyms for up/down if we enabled them;
// but that could be considered confusing; we also disable them in the main view.
@ -572,9 +617,6 @@ function _globalKeyPressHandler(actor, event) {
case Meta.KeyBindingAction.PANEL_MAIN_MENU:
overview.hide();
return true;
case Meta.KeyBindingAction.SWITCH_PANELS:
ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK);
return true;
}
return false;
@ -606,14 +648,17 @@ function _findModal(actor) {
* initiated event. If not provided then the value of
* global.get_current_time() is assumed.
*
* @options: optional Meta.ModalOptions flags to indicate that the
* pointer is alrady grabbed
*
* Returns: true iff we successfully acquired a grab or already had one
*/
function pushModal(actor, timestamp) {
function pushModal(actor, timestamp, options) {
if (timestamp == undefined)
timestamp = global.get_current_time();
if (modalCount == 0) {
if (!global.begin_modal(timestamp)) {
if (!global.begin_modal(timestamp, options ? options : 0)) {
log('pushModal: invocation of begin_modal failed');
return false;
}
@ -702,7 +747,6 @@ function popModal(actor, timestamp) {
function createLookingGlass() {
if (lookingGlass == null) {
lookingGlass = new LookingGlass.LookingGlass();
lookingGlass.slaveTo(panel.actor);
}
return lookingGlass;
}

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib;
@ -68,21 +68,24 @@ function _fixMarkup(text, allowMarkup) {
// Support &amp;, &quot;, &apos;, &lt; and &gt;, escape all other
// occurrences of '&'.
let _text = text.replace(/&(?!amp;|quot;|apos;|lt;|gt;)/g, '&amp;');
// Support <b>, <i>, and <u>, escape anything else
// so it displays as raw markup.
return _text.replace(/<(\/?[^biu]>|[^>\/][^>])/g, '&lt;$1');
} else {
// Escape everything
let _text = text.replace(/&/g, '&amp;');
return _text.replace(/</g, '&lt;');
_text = _text.replace(/<(?!\/?[biu]>)/g, '&lt;');
try {
Pango.parse_markup(_text, -1, '');
return _text;
} catch (e) {}
}
// !allowMarkup, or invalid markup
return GLib.markup_escape_text(text, -1);
}
function URLHighlighter(text, lineWrap, allowMarkup) {
this._init(text, lineWrap, allowMarkup);
}
const URLHighlighter = new Lang.Class({
Name: 'URLHighlighter',
URLHighlighter.prototype = {
_init: function(text, lineWrap, allowMarkup) {
if (!text)
text = '';
@ -106,12 +109,21 @@ URLHighlighter.prototype = {
this.setMarkup(text, allowMarkup);
this.actor.connect('button-press-event', Lang.bind(this, function(actor, event) {
// Don't try to URL highlight when invisible.
// The MessageTray doesn't actually hide us, so
// we need to check for paint opacities as well.
if (!actor.visible || actor.get_paint_opacity() == 0)
return false;
// Keep Notification.actor from seeing this and taking
// a pointer grab, which would block our button-release-event
// handler, if an URL is clicked
return this._findUrlAtPos(event) != -1;
}));
this.actor.connect('button-release-event', Lang.bind(this, function (actor, event) {
if (!actor.visible || actor.get_paint_opacity() == 0)
return false;
let urlId = this._findUrlAtPos(event);
if (urlId != -1) {
let url = this._urls[urlId].url;
@ -129,6 +141,9 @@ URLHighlighter.prototype = {
return false;
}));
this.actor.connect('motion-event', Lang.bind(this, function(actor, event) {
if (!actor.visible || actor.get_paint_opacity() == 0)
return false;
let urlId = this._findUrlAtPos(event);
if (urlId != -1 && !this._cursorChanged) {
global.set_cursor(Shell.Cursor.POINTING_HAND);
@ -140,6 +155,9 @@ URLHighlighter.prototype = {
return false;
}));
this.actor.connect('leave-event', Lang.bind(this, function() {
if (!this.actor.visible || this.actor.get_paint_opacity() == 0)
return;
if (this._cursorChanged) {
this._cursorChanged = false;
global.unset_cursor();
@ -191,13 +209,11 @@ URLHighlighter.prototype = {
}
return -1;
}
};
});
function FocusGrabber() {
this._init();
}
const FocusGrabber = new Lang.Class({
Name: 'FocusGrabber',
FocusGrabber.prototype = {
_init: function() {
this.actor = null;
@ -229,12 +245,11 @@ FocusGrabber.prototype = {
this.actor = actor;
let metaDisplay = global.screen.get_display();
this._prevFocusedWindow = metaDisplay.focus_window;
this._prevFocusedWindow = global.display.focus_window;
this._prevKeyFocusActor = global.stage.get_key_focus();
if (!Main.overview.visible)
if (global.stage_input_mode == Shell.StageInputMode.NONREACTIVE ||
global.stage_input_mode == Shell.StageInputMode.NORMAL)
global.set_stage_input_mode(Shell.StageInputMode.FOCUSED);
// Use captured-event to notice clicks outside the focused actor
@ -266,7 +281,8 @@ FocusGrabber.prototype = {
let source = event.get_source();
switch (event.type()) {
case Clutter.EventType.BUTTON_PRESS:
if (!this.actor.contains(source))
if (!this.actor.contains(source) &&
!Main.layoutManager.keyboardBox.contains(source))
this.emit('button-pressed', source);
break;
case Clutter.EventType.KEY_PRESS:
@ -285,8 +301,6 @@ FocusGrabber.prototype = {
if (!this._hasFocus)
return;
let metaDisplay = global.screen.get_display();
if (this._focusActorChangedId > 0) {
global.stage.disconnect(this._focusActorChangedId);
this._focusActorChangedId = 0;
@ -305,8 +319,8 @@ FocusGrabber.prototype = {
this._hasFocus = false;
this.emit('focus-ungrabbed');
if (this._prevFocusedWindow && !metaDisplay.focus_window) {
metaDisplay.set_input_focus_window(this._prevFocusedWindow, false, global.get_current_time());
if (this._prevFocusedWindow && !global.display.focus_window) {
global.display.set_input_focus_window(this._prevFocusedWindow, false, global.get_current_time());
this._prevFocusedWindow = null;
}
if (this._prevKeyFocusActor) {
@ -333,7 +347,7 @@ FocusGrabber.prototype = {
this._togglingFocusGrabMode = false;
}
}
}
});
Signals.addSignalMethods(FocusGrabber.prototype);
// Notification:
@ -390,13 +404,14 @@ Signals.addSignalMethods(FocusGrabber.prototype);
// the content and the action area of the notification will be cleared.
// The content area is also always cleared if 'customContent' is false
// because it might contain the @banner that didn't fit in the banner mode.
function Notification(source, title, banner, params) {
this._init(source, title, banner, params);
}
const Notification = new Lang.Class({
Name: 'Notification',
IMAGE_SIZE: 125,
Notification.prototype = {
_init: function(source, title, banner, params) {
this.source = source;
this.title = title;
this.urgency = Urgency.NORMAL;
this.resident = false;
// 'transient' is a reserved keyword in JS, so we have to use an alternate variable name
@ -411,6 +426,7 @@ Notification.prototype = {
this._titleDirection = St.TextDirection.NONE;
this._spacing = 0;
this._scrollPolicy = Gtk.PolicyType.AUTOMATIC;
this._imageBin = null;
source.connect('destroy', Lang.bind(this,
function (source, reason) {
@ -440,9 +456,19 @@ Notification.prototype = {
this._bannerBox.connect('allocate', Lang.bind(this, this._bannerBoxAllocate));
this._table.add(this._bannerBox, { row: 0,
col: 1,
col_span: 2,
x_expand: false,
y_expand: false,
y_fill: false });
// This is an empty cell that overlaps with this._bannerBox cell to ensure
// that this._bannerBox cell expands horizontally, while not forcing the
// this._imageBin that is also in col: 2 to expand horizontally.
this._table.add(new St.Bin(), { row: 0,
col: 2,
y_expand: false,
y_fill: false });
this._titleLabel = new St.Label();
this._bannerBox.add_actor(this._titleLabel);
this._bannerUrlHighlighter = new URLHighlighter();
@ -473,8 +499,11 @@ Notification.prototype = {
let oldFocus = global.stage.key_focus;
if (this._icon)
if (this._icon && (params.icon || params.clear)) {
this._icon.destroy();
this._icon = null;
}
// We always clear the content area if we don't have custom
// content because it might contain the @banner that didn't
// fit in the banner mode.
@ -494,17 +523,23 @@ Notification.prototype = {
this._actionArea = null;
this._buttonBox = null;
}
if (!this._scrollArea && !this._actionArea)
if (this._imageBin && params.clear)
this.unsetImage();
if (!this._scrollArea && !this._actionArea && !this._imageBin)
this._table.remove_style_class_name('multi-line-notification');
this._icon = params.icon || this.source.createNotificationIcon();
this._table.add(this._icon, { row: 0,
col: 0,
x_expand: false,
y_expand: false,
y_fill: false,
y_align: St.Align.START });
if (!this._icon) {
this._icon = params.icon || this.source.createNotificationIcon();
this._table.add(this._icon, { row: 0,
col: 0,
x_expand: false,
y_expand: false,
y_fill: false,
y_align: St.Align.START });
}
this.title = title;
title = title ? _fixMarkup(title.replace(/\n/g, ' '), params.titleMarkup) : '';
this._titleLabel.clutter_text.set_markup('<b>' + title + '</b>');
@ -556,9 +591,10 @@ Notification.prototype = {
this._table.add_style_class_name('multi-line-notification');
this._scrollArea = new St.ScrollView({ name: 'notification-scrollview',
vscrollbar_policy: this._scrollPolicy,
hscrollbar_policy: Gtk.PolicyType.NEVER,
style_class: 'vfade' });
this._table.add(this._scrollArea, { row: 1, col: 1 });
hscrollbar_policy: Gtk.PolicyType.NEVER });
this._table.add(this._scrollArea, { row: 1,
col: 2 });
this._updateLastColumnSettings();
this._contentArea = new St.BoxLayout({ name: 'notification-body',
vertical: true });
this._scrollArea.add_actor(this._contentArea);
@ -635,13 +671,53 @@ Notification.prototype = {
if (!props)
props = {};
props.row = 2;
props.col = 1;
props.col = 2;
this._table.add_style_class_name('multi-line-notification');
this._table.add(this._actionArea, props);
this._updateLastColumnSettings();
this._updated();
},
_updateLastColumnSettings: function() {
if (this._scrollArea)
this._table.child_set(this._scrollArea, { col: this._imageBin ? 2 : 1,
col_span: this._imageBin ? 1 : 2 });
if (this._actionArea)
this._table.child_set(this._actionArea, { col: this._imageBin ? 2 : 1,
col_span: this._imageBin ? 1 : 2 });
},
setImage: function(image) {
if (this._imageBin)
this.unsetImage();
this._imageBin = new St.Bin();
this._imageBin.child = image;
this._imageBin.opacity = 230;
this._table.add_style_class_name('multi-line-notification');
this._table.add_style_class_name('notification-with-image');
this._addBannerBody();
this._updateLastColumnSettings();
this._table.add(this._imageBin, { row: 1,
col: 1,
row_span: 2,
x_expand: false,
y_expand: false,
x_fill: false,
y_fill: false });
},
unsetImage: function() {
if (this._imageBin) {
this._table.remove_style_class_name('notification-with-image');
this._table.remove_actor(this._imageBin);
this._imageBin = null;
this._updateLastColumnSettings();
if (!this._scrollArea && !this._actionArea)
this._table.remove_style_class_name('multi-line-notification');
}
},
// addButton:
// @id: the action ID
// @label: the label for the action's button
@ -657,7 +733,9 @@ Notification.prototype = {
let box = new St.BoxLayout({ name: 'notification-actions' });
this.setActionArea(box, { x_expand: false,
y_expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END });
this._buttonBox = box;
}
@ -716,11 +794,12 @@ Notification.prototype = {
},
_bannerBoxAllocate: function(actor, box, flags) {
let [titleMinW, titleNatW] = this._titleLabel.get_preferred_width(-1);
let [titleMinH, titleNatH] = this._titleLabel.get_preferred_height(-1);
let [bannerMinW, bannerNatW] = this._bannerLabel.get_preferred_width(-1);
let availWidth = box.x2 - box.x1;
let [titleMinW, titleNatW] = this._titleLabel.get_preferred_width(-1);
let [titleMinH, titleNatH] = this._titleLabel.get_preferred_height(availWidth);
let [bannerMinW, bannerNatW] = this._bannerLabel.get_preferred_width(availWidth);
let titleBox = new Clutter.ActorBox();
let titleBoxW = Math.min(titleNatW, availWidth);
if (this._titleDirection == St.TextDirection.RTL) {
@ -868,14 +947,12 @@ Notification.prototype = {
this.actor.destroy();
this.actor._delegate = null;
}
};
});
Signals.addSignalMethods(Notification.prototype);
function Source(title) {
this._init(title);
}
const Source = new Lang.Class({
Name: 'MessageTraySource',
Source.prototype = {
ICON_SIZE: 24,
_init: function(title) {
@ -906,6 +983,7 @@ Source.prototype = {
this.isTransient = false;
this.isChat = false;
this.isMuted = false;
this.notifications = [];
},
@ -970,6 +1048,13 @@ Source.prototype = {
this.emit('title-changed');
},
setMuted: function(muted) {
if (!this.isChat || this.isMuted == muted)
return;
this.isMuted = muted;
this.emit('muted-changed');
},
// Called to create a new icon actor (of size this.ICON_SIZE).
// Must be overridden by the subclass if you do not pass icons
// explicitly to the Notification() constructor.
@ -1008,7 +1093,8 @@ Source.prototype = {
notify: function(notification) {
this.pushNotification(notification);
this.emit('notify', notification);
if (!this.isMuted)
this.emit('notify', notification);
},
destroy: function(reason) {
@ -1048,14 +1134,12 @@ Source.prototype = {
_lastNotificationRemoved: function() {
this.destroy();
}
};
});
Signals.addSignalMethods(Source.prototype);
function SummaryItem(source) {
this._init(source);
}
const SummaryItem = new Lang.Class({
Name: 'SummaryItem',
SummaryItem.prototype = {
_init: function(source) {
this.source = source;
this.source.connect('notification-added', Lang.bind(this, this._notificationAddedToSource));
@ -1094,9 +1178,7 @@ SummaryItem.prototype = {
this.notificationStack = new St.BoxLayout({ name: 'summary-notification-stack',
vertical: true });
this.notificationStackView.add_actor(this.notificationStack);
this._notificationExpandedIds = [];
this._notificationDoneDisplayingIds = [];
this._notificationDestroyedIds = [];
this._stackedNotifications = [];
this._oldMaxScrollAdjustment = 0;
@ -1126,6 +1208,18 @@ SummaryItem.prototype = {
}));
this.rightClickMenu.add(item.actor);
if (source.isChat) {
item = new PopupMenu.PopupMenuItem('');
item.actor.connect('notify::mapped', Lang.bind(this, function() {
item.label.set_text(source.isMuted ? _("Unmute") : _("Mute"));
}));
item.connect('activate', Lang.bind(this, function() {
source.setMuted(!source.isMuted);
this.emit('done-displaying-content');
}));
this.rightClickMenu.add(item.actor);
}
let focusManager = St.FocusManager.get_for_stage(global.stage);
focusManager.add_group(this.rightClickMenu);
},
@ -1165,18 +1259,19 @@ SummaryItem.prototype = {
doneShowingNotificationStack: function() {
let notificationActors = this.notificationStack.get_children();
for (let i = 0; i < notificationActors.length; i++) {
notificationActors[i]._delegate.collapseCompleted();
notificationActors[i]._delegate.disconnect(this._notificationExpandedIds[i]);
notificationActors[i]._delegate.disconnect(this._notificationDoneDisplayingIds[i]);
notificationActors[i]._delegate.disconnect(this._notificationDestroyedIds[i]);
this.notificationStack.remove_actor(notificationActors[i]);
notificationActors[i]._delegate.setIconVisible(true);
notificationActors[i]._delegate.enableScrolling(true);
for (let i = 0; i < this._stackedNotifications.length; i++) {
let stackedNotification = this._stackedNotifications[i];
let notification = stackedNotification.notification;
notification.collapseCompleted();
notification.disconnect(stackedNotification.notificationExpandedId);
notification.disconnect(stackedNotification.notificationDoneDisplayingId);
notification.disconnect(stackedNotification.notificationDestroyedId);
if (notification.actor.get_parent() == this.notificationStack)
this.notificationStack.remove_actor(notification.actor);
notification.setIconVisible(true);
notification.enableScrolling(true);
}
this._notificationExpandedIds = [];
this._notificationDoneDisplayingIds = [];
this._notificationDestroyedIds = [];
this._stackedNotifications = [];
},
_notificationAddedToSource: function(source, notification) {
@ -1185,12 +1280,12 @@ SummaryItem.prototype = {
},
_appendNotificationToStack: function(notification) {
let notificationExpandedId = notification.connect('expanded', Lang.bind(this, this._contentUpdated));
this._notificationExpandedIds.push(notificationExpandedId);
let notificationDoneDisplayingId = notification.connect('done-displaying', Lang.bind(this, this._notificationDoneDisplaying));
this._notificationDoneDisplayingIds.push(notificationDoneDisplayingId);
let notificationDestroyedId = notification.connect('destroy', Lang.bind(this, this._notificationDestroyed));
this._notificationDestroyedIds.push(notificationDestroyedId);
let stackedNotification = {};
stackedNotification.notification = notification;
stackedNotification.notificationExpandedId = notification.connect('expanded', Lang.bind(this, this._contentUpdated));
stackedNotification.notificationDoneDisplayingId = notification.connect('done-displaying', Lang.bind(this, this._notificationDoneDisplaying));
stackedNotification.notificationDestroyedId = notification.connect('destroy', Lang.bind(this, this._notificationDestroyed));
this._stackedNotifications.push(stackedNotification);
if (!this.source.isChat)
notification.enableScrolling(false);
if (this.notificationStack.get_children().length > 0)
@ -1220,35 +1315,37 @@ SummaryItem.prototype = {
},
_notificationDestroyed: function(notification) {
let index = this.notificationStack.get_children().indexOf(notification.actor);
if (index >= 0) {
notification.disconnect(this._notificationExpandedIds[index]);
this._notificationExpandedIds.splice(index, 1);
notification.disconnect(this._notificationDoneDisplayingIds[index]);
this._notificationDoneDisplayingIds.splice(index, 1);
notification.disconnect(this._notificationDestroyedIds[index]);
this._notificationDestroyedIds.splice(index, 1);
this.notificationStack.remove_actor(notification.actor);
this._contentUpdated();
for (let i = 0; i < this._stackedNotifications.length; i++) {
if (this._stackedNotifications[i].notification == notification) {
let stackedNotification = this._stackedNotifications[i];
notification.disconnect(stackedNotification.notificationExpandedId);
notification.disconnect(stackedNotification.notificationDoneDisplayingId);
notification.disconnect(stackedNotification.notificationDestroyedId);
this._stackedNotifications.splice(i, 1);
this._contentUpdated();
break;
}
}
if (this.notificationStack.get_children().length > 0)
this.notificationStack.get_children()[0]._delegate.setIconVisible(true);
}
};
});
Signals.addSignalMethods(SummaryItem.prototype);
function MessageTray() {
this._init();
}
const MessageTray = new Lang.Class({
Name: 'MessageTray',
MessageTray.prototype = {
_init: function() {
this._presence = new GnomeSession.Presence();
this._presence = new GnomeSession.Presence(Lang.bind(this, function(proxy, error) {
this._onStatusChanged(proxy.status);
}));
this._userStatus = GnomeSession.PresenceStatus.AVAILABLE;
this._busy = false;
this._backFromAway = false;
this._presence.connect('StatusChanged', Lang.bind(this, this._onStatusChanged));
this._presence.getStatus(Lang.bind(this, this._onStatusChanged));
this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) {
this._onStatusChanged(status);
}));
this.actor = new St.Group({ name: 'message-tray',
reactive: true,
@ -1272,14 +1369,14 @@ MessageTray.prototype = {
this._summaryBin.opacity = 0;
this._summaryMotionId = 0;
this._trayMotionId = 0;
this._summaryBoxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
{ reactive: true,
track_hover: true });
{ reactive: true,
track_hover: true });
this._summaryBoxPointer.actor.style_class = 'summary-boxpointer';
this.actor.add_actor(this._summaryBoxPointer.actor);
this._summaryBoxPointer.actor.lower_bottom();
this._summaryBoxPointer.actor.hide();
Main.layoutManager.addChrome(this._summaryBoxPointer.actor, { visibleInFullscreen: true });
this._summaryBoxPointerItem = null;
this._summaryBoxPointerContentUpdatedId = 0;
@ -1312,11 +1409,15 @@ MessageTray.prototype = {
}));
this._focusGrabber.connect('escape-pressed', Lang.bind(this, this._escapeTray));
Main.layoutManager.keyboardBox.connect('notify::hover', Lang.bind(this, this._onKeyboardHoverChanged));
this._trayState = State.HIDDEN;
this._locked = false;
this._traySummoned = false;
this._useLongerTrayLeftTimeout = false;
this._trayLeftTimeoutId = 0;
this._pointerInTray = false;
this._pointerInKeyboard = false;
this._summaryState = State.HIDDEN;
this._summaryTimeoutId = 0;
this._pointerInSummary = false;
@ -1329,10 +1430,18 @@ MessageTray.prototype = {
this._notificationRemoved = false;
this._reNotifyAfterHideNotification = null;
Main.chrome.addActor(this.actor, { affectsStruts: false,
visibleInFullscreen: true });
Main.chrome.trackActor(this._notificationBin);
Main.chrome.trackActor(this._summaryBoxPointer.actor);
this._corner = new Clutter.Rectangle({ width: 1,
height: 1,
opacity: 0,
reactive: true });
this._corner.connect('enter-event', Lang.bind(this, this._onCornerEnter));
Main.layoutManager.trayBox.add_actor(this._corner);
Main.layoutManager.trackChrome(this._corner);
Main.layoutManager.trayBox.add_actor(this.actor);
this.actor.y = this.actor.height;
Main.layoutManager.trackChrome(this.actor);
Main.layoutManager.trackChrome(this._notificationBin);
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._setSizePosition));
@ -1369,24 +1478,24 @@ MessageTray.prototype = {
this._chatSummaryItemsCount = 0;
},
_onCornerEnter: function(actor, event) {
this._pointerInSummary = true;
this._updateState();
},
_setSizePosition: function() {
let monitor = Main.layoutManager.bottomMonitor;
this.actor.x = monitor.x;
this.actor.y = monitor.y + monitor.height - 1;
this.actor.width = monitor.width;
this._notificationBin.x = 0;
this._notificationBin.width = monitor.width;
this._summaryBin.x = 0;
this._summaryBin.width = monitor.width;
if (this._pointerBarrier)
global.destroy_pointer_barrier(this._pointerBarrier);
this._pointerBarrier =
global.create_pointer_barrier(monitor.x + monitor.width, monitor.y + monitor.height - this.actor.height,
monitor.x + monitor.width, monitor.y + monitor.height,
4 /* BarrierNegativeX */);
if (St.Widget.get_default_direction() == St.TextDirection.RTL)
this._corner.x = 0;
else
this._corner.x = Main.layoutManager.trayBox.width - 1;
this._corner.y = Main.layoutManager.trayBox.height - 1;
},
contains: function(source) {
@ -1431,12 +1540,23 @@ MessageTray.prototype = {
// after notifications are done showing. However, we don't want that to happen for
// transient sources, which are removed after the notification is shown, but are
// not removed fast enough because of the callbacks to avoid the summary popping up.
// So we just don't add transient sources to this._newSummaryItems .
if (!source.isTransient)
// So we just don't add transient sources to this._newSummaryItems.
// We don't want that to happen for chat sources neither, because they
// can be added when the user starts a chat from Empathy and they are not transient.
// The notification will popup on incoming message anyway. See bug #657249.
if (!source.isTransient && !source.isChat)
this._newSummaryItems.push(summaryItem);
source.connect('notify', Lang.bind(this, this._onNotify));
source.connect('muted-changed', Lang.bind(this,
function () {
if (source.isMuted)
this._notificationQueue = this._notificationQueue.filter(function(notification) {
return source != notification.source;
});
}));
summaryItem.actor.connect('notify::hover', Lang.bind(this,
function () {
this._onSummaryItemHoverChanged(summaryItem);
@ -1506,7 +1626,7 @@ MessageTray.prototype = {
summaryItemToRemove.actor.destroy();
if (needUpdate);
if (needUpdate)
this._updateState();
},
@ -1532,7 +1652,19 @@ MessageTray.prototype = {
if (!this._locked)
return;
this._locked = false;
this._pointerInTray = this.actor.hover && !this._summaryBoxPointer.bin.hover;
this._pointerInTray = this.actor.hover;
this._updateState();
},
toggle: function() {
this._traySummoned = !this._traySummoned;
this._updateState();
},
hide: function() {
this._traySummoned = false;
this.actor.set_hover(false);
this._summary.set_hover(false);
this._updateState();
},
@ -1720,13 +1852,6 @@ MessageTray.prototype = {
if (this._useLongerTrayLeftTimeout && !this._trayLeftTimeoutId)
return;
// Don't do anything if the mouse is over the summary notification as this should be considered as
// leaving the tray. The tray is locked when the summary notification is visible anyway, but we
// should treat the mouse being over the summary notification as the tray being left for collapsing
// any expanded summary item other than the one related to the notification.
if (this._summaryBoxPointer.bin.hover)
return;
this._useLongerTrayLeftTimeout = false;
if (this._trayLeftTimeoutId) {
Mainloop.source_remove(this._trayLeftTimeoutId);
@ -1746,7 +1871,7 @@ MessageTray.prototype = {
// automatically. Instead, the user is able to expand the notification by mousing away from it and then
// mousing back in. Because this is an expected action, we set the boolean flag that indicates that a longer
// timeout should be used before popping down the notification.
if (this._notificationBin.contains(actorAtShowNotificationPosition)) {
if (this.actor.contains(actorAtShowNotificationPosition)) {
this._useLongerTrayLeftTimeout = true;
return;
}
@ -1770,7 +1895,25 @@ MessageTray.prototype = {
}
},
_onStatusChanged: function(presence, status) {
_onKeyboardHoverChanged: function(keyboard) {
this._pointerInKeyboard = keyboard.hover;
if (!keyboard.hover) {
let event = Clutter.get_current_event();
if (event && event.type() == Clutter.EventType.LEAVE) {
let into = event.get_related();
if (into && this.actor.contains(into)) {
// Don't call _updateState, because pointerInTray is
// still false
return;
}
}
}
this._updateState();
},
_onStatusChanged: function(status) {
this._backFromAway = (this._userStatus == GnomeSession.PresenceStatus.IDLE && this._userStatus != status);
this._userStatus = status;
@ -1834,7 +1977,7 @@ MessageTray.prototype = {
let notificationsPending = this._notificationQueue.length > 0 && (!this._busy || 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._notificationRemoved;
let notificationExpired = (this._notificationTimeoutId == 0 && !(this._notification && this._notification.urgency == Urgency.CRITICAL) && !this._pointerInTray && !this._locked && !(this._pointerInKeyboard && notificationExpanded)) || this._notificationRemoved;
let canShowNotification = notificationsPending && this._summaryState == State.HIDDEN;
if (this._notificationState == State.HIDDEN) {
@ -1850,7 +1993,7 @@ MessageTray.prototype = {
}
// Summary
let summarySummoned = this._pointerInSummary || this._overviewVisible;
let summarySummoned = this._pointerInSummary || this._overviewVisible || this._traySummoned;
let summaryPinned = this._summaryTimeoutId != 0 || this._pointerInTray || summarySummoned || this._locked;
let summaryHovered = this._pointerInTray || this._pointerInSummary;
let summaryVisibleWithNoHover = (this._overviewVisible || this._locked) && !summaryHovered;
@ -1905,8 +2048,11 @@ MessageTray.prototype = {
if (haveClickedSummaryItem && !summarySourceIsMainNotificationSource && canShowSummaryBoxPointer && !requestedNotificationStackIsEmpty)
this._showSummaryBoxPointer();
} else if (this._summaryBoxPointerState == State.SHOWN) {
if (!haveClickedSummaryItem || !canShowSummaryBoxPointer || wrongSummaryBoxPointer || mustHideSummary)
if (!haveClickedSummaryItem || !canShowSummaryBoxPointer || wrongSummaryBoxPointer || mustHideSummary) {
this._hideSummaryBoxPointer();
if (wrongSummaryBoxPointer)
this._showSummaryBoxPointer();
}
}
// Tray itself
@ -1944,18 +2090,16 @@ MessageTray.prototype = {
},
_showTray: function() {
let monitor = Main.layoutManager.bottomMonitor;
this._tween(this.actor, '_trayState', State.SHOWN,
{ y: monitor.y + monitor.height - this.actor.height,
{ y: -this.actor.height,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
_hideTray: function() {
let monitor = Main.layoutManager.bottomMonitor;
this._tween(this.actor, '_trayState', State.HIDDEN,
{ y: monitor.y + monitor.height - 1,
{ y: this.actor.height,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
@ -2043,7 +2187,7 @@ MessageTray.prototype = {
_notificationTimeout: function() {
let [x, y, mods] = global.get_pointer();
if (y > this._lastSeenMouseY + 10 && y < this.actor.y) {
if (y > this._lastSeenMouseY + 10 && !this.actor.hover) {
// The mouse is moving towards the notification, so don't
// hide it yet. (We just create a new timeout (and destroy
// the old one) each time because the bookkeeping is
@ -2171,7 +2315,7 @@ MessageTray.prototype = {
_showSummaryBoxPointer: function() {
this._summaryBoxPointerItem = this._clickedSummaryItem;
this._summaryBoxPointerContentUpdatedId = this._summaryBoxPointerItem.connect('content-updated',
Lang.bind(this, this._adjustSummaryBoxPointerPosition));
Lang.bind(this, this._onSummaryBoxPointerContentUpdated));
this._summaryBoxPointerDoneDisplayingId = this._summaryBoxPointerItem.connect('done-displaying-content',
Lang.bind(this, this._escapeTray));
if (this._clickedSummaryItemMouseButton == 1) {
@ -2194,6 +2338,8 @@ MessageTray.prototype = {
// _clickedSummaryItem.actor can change absolute position without changing allocation
this._summaryMotionId = this._summary.connect('allocation-changed',
Lang.bind(this, this._adjustSummaryBoxPointerPosition));
this._trayMotionId = Main.layoutManager.trayBox.connect('notify::anchor-y',
Lang.bind(this, this._adjustSummaryBoxPointerPosition));
this._summaryBoxPointer.actor.opacity = 0;
this._summaryBoxPointer.actor.show();
@ -2206,6 +2352,13 @@ MessageTray.prototype = {
}));
},
_onSummaryBoxPointerContentUpdated: function() {
if (this._summaryBoxPointerItem.notificationStack.get_children().length == 0)
this._hideSummaryBoxPointer();
this._adjustSummaryBoxPointerPosition();
},
_adjustSummaryBoxPointerPosition: function() {
// The position of the arrow origin should be the same as center of this._clickedSummaryItem.actor
if (!this._clickedSummaryItem)
@ -2218,8 +2371,10 @@ MessageTray.prototype = {
if (this._clickedSummaryItemAllocationChangedId) {
this._clickedSummaryItem.actor.disconnect(this._clickedSummaryItemAllocationChangedId);
this._summary.disconnect(this._summaryMotionId);
Main.layoutManager.trayBox.disconnect(this._trayMotionId);
this._clickedSummaryItemAllocationChangedId = 0;
this._summaryMotionId = 0;
this._trayMotionId = 0;
}
if (this._clickedSummaryItem)
@ -2229,6 +2384,14 @@ MessageTray.prototype = {
},
_hideSummaryBoxPointer: function() {
// We should be sure to hide the box pointer if all notifications in it are destroyed while
// it is hiding, so that we don't show an an animation of an empty blob being hidden.
if (this._summaryBoxPointerState == State.HIDING &&
this._summaryBoxPointerItem.notificationStack.get_children().length == 0) {
this._summaryBoxPointer.actor.hide();
return;
}
this._summaryBoxPointerState = State.HIDING;
// Unset this._clickedSummaryItem if we are no longer showing the summary
if (this._summaryState != State.SHOWN)
@ -2273,17 +2436,14 @@ MessageTray.prototype = {
if (this._clickedSummaryItem)
this._updateState();
}
};
});
function SystemNotificationSource() {
this._init();
}
SystemNotificationSource.prototype = {
__proto__: Source.prototype,
const SystemNotificationSource = new Lang.Class({
Name: 'SystemNotificationSource',
Extends: Source,
_init: function() {
Source.prototype._init.call(this, _("System Information"));
this.parent(_("System Information"));
this._setSummaryIcon(this.createNotificationIcon());
},
@ -2297,4 +2457,4 @@ SystemNotificationSource.prototype = {
open: function() {
this.destroy();
}
};
});

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gdk = imports.gi.Gdk;
@ -18,6 +18,7 @@ const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const OPEN_AND_CLOSE_TIME = 0.1;
const FADE_IN_BUTTONS_TIME = 0.33;
const FADE_OUT_DIALOG_TIME = 1.0;
const State = {
@ -28,16 +29,16 @@ const State = {
FADED_OUT: 4
};
function ModalDialog() {
this._init();
}
const ModalDialog = new Lang.Class({
Name: 'ModalDialog',
ModalDialog.prototype = {
_init: function(params) {
params = Params.parse(params, { styleClass: null });
params = Params.parse(params, { shellReactive: false,
styleClass: null });
this.state = State.CLOSED;
this._hasModal = false;
this._shellReactive = params.shellReactive;
this._group = new St.Group({ visible: false,
x: 0,
@ -53,26 +54,30 @@ ModalDialog.prototype = {
this._actionKeys = {};
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
this._lightbox = new Lightbox.Lightbox(this._group,
{ inhibitEvents: true });
this._backgroundBin = new St.Bin();
this._group.add_actor(this._backgroundBin);
this._lightbox.highlight(this._backgroundBin);
this._backgroundStack = new Shell.Stack();
this._backgroundBin.child = this._backgroundStack;
this._eventBlocker = new Clutter.Group({ reactive: true });
this._backgroundStack.add_actor(this._eventBlocker);
this._dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
vertical: true });
if (params.styleClass != null) {
this._dialogLayout.add_style_class_name(params.styleClass);
}
this._backgroundStack.add_actor(this._dialogLayout);
if (!this._shellReactive) {
this._lightbox = new Lightbox.Lightbox(this._group,
{ inhibitEvents: true });
this._lightbox.highlight(this._backgroundBin);
let stack = new Shell.Stack();
this._backgroundBin.child = stack;
this._eventBlocker = new Clutter.Group({ reactive: true });
stack.add_actor(this._eventBlocker);
stack.add_actor(this._dialogLayout);
} else {
this._backgroundBin.child = this._dialogLayout;
}
this.contentLayout = new St.BoxLayout({ vertical: true });
this._dialogLayout.add(this.contentLayout,
@ -82,7 +87,6 @@ ModalDialog.prototype = {
y_align: St.Align.START });
this._buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
opacity: 220,
vertical: false });
this._dialogLayout.add(this._buttonLayout,
{ expand: true,
@ -94,21 +98,26 @@ ModalDialog.prototype = {
this._savedKeyFocus = null;
},
destroy: function() {
this._group.destroy();
},
setButtons: function(buttons) {
let hadChildren = this._buttonLayout.get_children() > 0;
this._buttonLayout.destroy_children();
this._actionKeys = {};
let i = 0;
for (let index in buttons) {
let buttonInfo = buttons[index];
for (let i = 0; i < buttons.length; i++) {
let buttonInfo = buttons[i];
let label = buttonInfo['label'];
let action = buttonInfo['action'];
let key = buttonInfo['key'];
let button = new St.Button({ style_class: 'modal-dialog-button',
reactive: true,
can_focus: true,
label: label });
buttonInfo.button = new St.Button({ style_class: 'modal-dialog-button',
reactive: true,
can_focus: true,
label: label });
let x_alignment;
if (buttons.length == 1)
@ -120,20 +129,37 @@ ModalDialog.prototype = {
else
x_alignment = St.Align.MIDDLE;
this._initialKeyFocus = button;
this._buttonLayout.add(button,
if (this._initialKeyFocus == this._dialogLayout ||
this._buttonLayout.contains(this._initialKeyFocus))
this._initialKeyFocus = buttonInfo.button;
this._buttonLayout.add(buttonInfo.button,
{ expand: true,
x_fill: false,
y_fill: false,
x_align: x_alignment,
y_align: St.Align.MIDDLE });
button.connect('clicked', action);
buttonInfo.button.connect('clicked', action);
if (key)
this._actionKeys[key] = action;
i++;
}
// Fade in buttons if there weren't any before
if (!hadChildren && buttons.length > 0) {
this._buttonLayout.opacity = 0;
Tweener.addTween(this._buttonLayout,
{ opacity: 255,
time: FADE_IN_BUTTONS_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.emit('buttons-set');
})
});
} else {
this.emit('buttons-set');
}
},
_onKeyPressEvent: function(object, keyPressEvent) {
@ -157,7 +183,8 @@ ModalDialog.prototype = {
this.state = State.OPENING;
this._dialogLayout.opacity = 255;
this._lightbox.show();
if (this._lightbox)
this._lightbox.show();
this._group.opacity = 0;
this._group.show();
Tweener.addTween(this._group,
@ -223,7 +250,8 @@ ModalDialog.prototype = {
global.gdk_screen.get_display().sync();
this._hasModal = false;
this._eventBlocker.raise_top();
if (!this._shellReactive)
this._eventBlocker.raise_top();
},
pushModal: function (timestamp) {
@ -239,7 +267,8 @@ ModalDialog.prototype = {
} else
this._initialKeyFocus.grab_key_focus();
this._eventBlocker.lower_bottom();
if (!this._shellReactive)
this._eventBlocker.lower_bottom();
return true;
},
@ -272,5 +301,5 @@ ModalDialog.prototype = {
})
});
}
};
});
Signals.addSignalMethods(ModalDialog.prototype);

385
js/ui/networkAgent.js Normal file
View File

@ -0,0 +1,385 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/*
* Copyright 2011 Giovanni Campagna <scampa.giovanni@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
*/
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const NetworkManager = imports.gi.NetworkManager;
const NMClient = imports.gi.NMClient;
const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const ModalDialog = imports.ui.modalDialog;
const PopupMenu = imports.ui.popupMenu;
const ShellEntry = imports.ui.shellEntry;
const NetworkSecretDialog = new Lang.Class({
Name: 'NetworkSecretDialog',
Extends: ModalDialog.ModalDialog,
_init: function(agent, requestId, connection, settingName, hints) {
this.parent({ styleClass: 'polkit-dialog' });
this._agent = agent;
this._requestId = requestId;
this._connection = connection;
this._settingName = settingName;
this._hints = hints;
this._content = this._getContent();
let mainContentBox = new St.BoxLayout({ style_class: 'polkit-dialog-main-layout',
vertical: false });
this.contentLayout.add(mainContentBox,
{ x_fill: true,
y_fill: true });
let icon = new St.Icon({ icon_name: 'dialog-password-symbolic' });
mainContentBox.add(icon,
{ x_fill: true,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.START });
let messageBox = new St.BoxLayout({ style_class: 'polkit-dialog-message-layout',
vertical: true });
mainContentBox.add(messageBox,
{ y_align: St.Align.START });
let subjectLabel = new St.Label({ style_class: 'polkit-dialog-headline',
text: this._content.title });
messageBox.add(subjectLabel,
{ y_fill: false,
y_align: St.Align.START });
if (this._content.message != null) {
let descriptionLabel = new St.Label({ style_class: 'polkit-dialog-description',
text: this._content.message,
// HACK: for reasons unknown to me, the label
// is not asked the correct height for width,
// and thus is underallocated
// place a fixed height to avoid overflowing
style: 'height: 3em'
});
descriptionLabel.clutter_text.line_wrap = true;
messageBox.add(descriptionLabel,
{ y_fill: true,
y_align: St.Align.START,
expand: true });
}
let secretTable = new St.Table({ style_class: 'network-dialog-secret-table' });
let initialFocusSet = false;
let pos = 0;
for (let i = 0; i < this._content.secrets.length; i++) {
let secret = this._content.secrets[i];
let label = new St.Label({ style_class: 'polkit-dialog-password-label',
text: secret.label });
let reactive = secret.key != null;
secret.entry = new St.Entry({ style_class: 'polkit-dialog-password-entry',
text: secret.value, can_focus: reactive,
reactive: reactive });
ShellEntry.addContextMenu(secret.entry,
{ isPassword: secret.password });
if (secret.validate)
secret.valid = secret.validate(secret);
else // no special validation, just ensure it's not empty
secret.valid = secret.value.length > 0;
if (reactive) {
if (!initialFocusSet) {
this.setInitialKeyFocus(secret.entry);
initialFocusSet = true;
}
secret.entry.clutter_text.connect('activate', Lang.bind(this, this._onOk));
secret.entry.clutter_text.connect('text-changed', Lang.bind(this, function() {
secret.value = secret.entry.get_text();
if (secret.validate)
secret.valid = secret.validate(secret);
else
secret.valid = secret.value.length > 0;
this._updateOkButton();
}));
} else
secret.valid = true;
secretTable.add(label, { row: pos, col: 0, x_expand: false, x_fill: true, x_align: St.Align.START, y_align: St.Align.START });
secretTable.add(secret.entry, { row: pos, col: 1, x_expand: true, x_fill: true, y_align: St.Align.END });
pos++;
if (secret.password)
secret.entry.clutter_text.set_password_char('\u25cf');
}
messageBox.add(secretTable);
this._okButton = { label: _("Connect"),
action: Lang.bind(this, this._onOk),
key: Clutter.KEY_Return,
};
this.setButtons([{ label: _("Cancel"),
action: Lang.bind(this, this.cancel),
key: Clutter.KEY_Escape,
},
this._okButton]);
},
_updateOkButton: function() {
let valid = true;
for (let i = 0; i < this._content.secrets.length; i++) {
let secret = this._content.secrets[i];
valid = valid && secret.valid;
}
this._okButton.button.reactive = valid;
this._okButton.button.can_focus = valid;
if (valid)
this._okButton.button.remove_style_pseudo_class('disabled');
else
this._okButton.button.add_style_pseudo_class('disabled');
},
_onOk: function() {
let valid = true;
for (let i = 0; i < this._content.secrets.length; i++) {
let secret = this._content.secrets[i];
valid = valid && secret.valid;
if (secret.key != null)
this._agent.set_password(this._requestId, secret.key, secret.value);
}
if (valid) {
this._agent.respond(this._requestId, false);
this.close(global.get_current_time());
}
// do nothing if not valid
},
cancel: function() {
this._agent.respond(this._requestId, true);
this.close(global.get_current_time());
},
_validateWpaPsk: function(secret) {
let value = secret.value;
if (value.length == 64) {
// must be composed of hexadecimal digits only
for (let i = 0; i < 64; i++) {
if (!((value[i] >= 'a' && value[i] <= 'f')
|| (value[i] >= 'A' && value[i] <= 'F')
|| (value[i] >= '0' && value[i] <= '9')))
return false;
}
return true;
}
return (value.length >= 8 && value.length <= 63);
},
_validateStaticWep: function(secret) {
let value = secret.value;
if (secret.wep_key_type == NetworkManager.WepKeyType.KEY) {
if (value.length == 10 || value.length == 26) {
for (let i = 0; i < value.length; i++) {
if (!((value[i] >= 'a' && value[i] <= 'f')
|| (value[i] >= 'A' && value[i] <= 'F')
|| (value[i] >= '0' && value[i] <= '9')))
return false;
}
} else if (value.length == 5 || value.length == 13) {
for (let i = 0; i < value.length; i++) {
if (!((value[i] >= 'a' && value[i] <= 'z')
|| (value[i] >= 'A' && value[i] <= 'Z')))
return false;
}
} else
return false;
} else if (secret.wep_key_type == NetworkManager.WepKeyType.PASSPHRASE) {
if (value.length < 0 || value.length > 64)
return false;
}
return true;
},
_getWirelessSecrets: function(secrets, wirelessSetting) {
let wirelessSecuritySetting = this._connection.get_setting_wireless_security();
switch (wirelessSecuritySetting.key_mgmt) {
// First the easy ones
case 'wpa-none':
case 'wpa-psk':
secrets.push({ label: _("Password: "), key: 'psk',
value: wirelessSecuritySetting.psk || '',
validate: this._validateWpaPsk, password: true });
break;
case 'none': // static WEP
secrets.push({ label: _("Key: "), key: 'wep-key' + wirelessSecuritySetting.wep_tx_keyidx,
value: wirelessSecuritySetting.get_wep_key(wirelessSecuritySetting.wep_tx_keyidx) || '',
wep_key_type: wirelessSecuritySetting.wep_key_type,
validate: this._validateStaticWep, password: true });
break;
case 'ieee8021x':
if (wirelessSecuritySetting.auth_alg == 'leap') // Cisco LEAP
secrets.push({ label: _("Password: "), key: 'leap-password',
value: wirelessSecuritySetting.leap_password || '', password: true });
else // Dynamic (IEEE 802.1x) WEP
this._get8021xSecrets(secrets);
break;
case 'wpa-eap':
this._get8021xSecrets(secrets);
break;
default:
log('Invalid wireless key management: ' + wirelessSecuritySetting.key_mgmt);
}
},
_get8021xSecrets: function(secrets) {
let ieee8021xSetting = this._connection.get_setting_802_1x();
let phase2method;
switch (ieee8021xSetting.get_eap_method(0)) {
case 'md5':
case 'leap':
case 'ttls':
case 'peap':
// TTLS and PEAP are actually much more complicated, but this complication
// is not visible here since we only care about phase2 authentication
// (and don't even care of which one)
secrets.push({ label: _("Username: "), key: null,
value: ieee8021xSetting.identity || '', password: false });
secrets.push({ label: _("Password: "), key: 'password',
value: ieee8021xSetting.password || '', password: true });
break;
case 'tls':
secrets.push({ label: _("Identity: "), key: null,
value: ieee8021xSetting.identity || '', password: false });
secrets.push({ label: _("Private key password: "), key: 'private-key-password',
value: ieee8021xSetting.private_key_password || '', password: true });
break;
default:
log('Invalid EAP/IEEE802.1x method: ' + ieee8021xSetting.get_eap_method(0));
}
},
_getPPPoESecrets: function(secrets) {
let pppoeSetting = this._connection.get_setting_pppoe();
secrets.push({ label: _("Username: "), key: 'username',
value: pppoeSetting.username || '', password: false });
secrets.push({ label: _("Service: "), key: 'service',
value: pppoeSetting.service || '', password: false });
secrets.push({ label: _("Password: "), key: 'password',
value: pppoeSetting.password || '', password: true });
},
_getMobileSecrets: function(secrets, connectionType) {
let setting;
if (connectionType == 'bluetooth')
setting = this._connection.get_setting_cdma() || this._connection.get_setting_gsm();
else
setting = this._connection.get_setting_by_name(connectionType);
secrets.push({ label: _("Password: "), key: 'password',
value: setting.value || '', password: true });
},
_getContent: function() {
let connectionSetting = this._connection.get_setting_connection();
let connectionType = connectionSetting.get_connection_type();
let wirelessSetting;
let ssid;
let content = { };
content.secrets = [ ];
switch (connectionType) {
case '802-11-wireless':
wirelessSetting = this._connection.get_setting_wireless();
ssid = NetworkManager.utils_ssid_to_utf8(wirelessSetting.get_ssid());
content.title = _("Authentication required by wireless network");
content.message = _("Passwords or encryption keys are required to access the wireless network '%s'.").format(ssid);
this._getWirelessSecrets(content.secrets, wirelessSetting);
break;
case '802-3-ethernet':
content.title = _("Wired 802.1X authentication");
content.message = null;
content.secrets.push({ label: _("Network name: "), key: null,
value: connectionSetting.get_id(), password: false });
this._get8021xSecrets(content.secrets);
break;
case 'pppoe':
content.title = _("DSL authentication");
content.message = null;
this._getPPPoESecrets(content.secrets);
break;
case 'gsm':
if (this._hints.indexOf('pin') != -1) {
let gsmSetting = this._connection.get_setting_gsm();
content.title = _("PIN code required");
content.message = _("PIN code is needed for the mobile broadband device");
content.secrets.push({ label: _("PIN: "), key: 'pin',
value: gsmSetting.pin || '', password: true });
}
// fall through
case 'cdma':
case 'bluetooth':
content.title = _("Mobile broadband network password");
content.message = _("A password is required to connect to '%s'.").format(connectionSetting.get_id());
this._getMobileSecrets(content.secrets, connectionType);
break;
default:
log('Invalid connection type: ' + connectionType);
};
return content;
}
});
const NetworkAgent = new Lang.Class({
Name: 'NetworkAgent',
_init: function() {
this._native = new Shell.NetworkAgent({ auto_register: true,
identifier: 'org.gnome.Shell.NetworkAgent' });
this._dialogs = { };
this._native.connect('new-request', Lang.bind(this, this._newRequest));
this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest));
},
_newRequest: function(agent, requestId, connection, settingName, hints) {
let dialog = new NetworkSecretDialog(agent, requestId, connection, settingName, hints);
dialog.connect('destroy', Lang.bind(this, function() {
delete this._dialogs[requestId];
}));
this._dialogs[requestId] = dialog;
dialog.open(global.get_current_time());
},
_cancelRequest: function(agent, requestId) {
this._dialogs[requestId].close(global.get_current_time());
this._dialogs[requestId].destroy();
}
});

View File

@ -1,7 +1,7 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const DBus = imports.dbus;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
@ -16,49 +16,52 @@ const Util = imports.misc.util;
let nextNotificationId = 1;
// Should really be defined in dbus.js
const BusIface = {
name: 'org.freedesktop.DBus',
methods: [{ name: 'GetConnectionUnixProcessID',
inSignature: 's',
outSignature: 'i' }]
};
// Should really be defined in Gio.js
const BusIface = <interface name="org.freedesktop.DBus">
<method name="GetConnectionUnixProcessID">
<arg type="s" direction="in" />
<arg type="u" direction="out" />
</method>
</interface>;
const Bus = function () {
this._init();
};
var BusProxy = Gio.DBusProxy.makeProxyWrapper(BusIface);
function Bus() {
return new BusProxy(Gio.DBus.session, 'org.freedesktop.DBus', '/org/freedesktop/DBus');
}
Bus.prototype = {
_init: function() {
DBus.session.proxifyObject(this, 'org.freedesktop.DBus', '/org/freedesktop/DBus');
}
};
DBus.proxifyPrototype(Bus.prototype, BusIface);
const NotificationDaemonIface = {
name: 'org.freedesktop.Notifications',
methods: [{ name: 'Notify',
inSignature: 'susssasa{sv}i',
outSignature: 'u'
},
{ name: 'CloseNotification',
inSignature: 'u',
outSignature: ''
},
{ name: 'GetCapabilities',
inSignature: '',
outSignature: 'as'
},
{ name: 'GetServerInformation',
inSignature: '',
outSignature: 'ssss'
}],
signals: [{ name: 'NotificationClosed',
inSignature: 'uu' },
{ name: 'ActionInvoked',
inSignature: 'us' }]
};
const NotificationDaemonIface = <interface name="org.freedesktop.Notifications">
<method name="Notify">
<arg type="s" direction="in"/>
<arg type="u" direction="in"/>
<arg type="s" direction="in"/>
<arg type="s" direction="in"/>
<arg type="s" direction="in"/>
<arg type="as" direction="in"/>
<arg type="a{sv}" direction="in"/>
<arg type="i" direction="in"/>
<arg type="u" direction="out"/>
</method>
<method name="CloseNotification">
<arg type="u" direction="in"/>
</method>
<method name="GetCapabilities">
<arg type="as" direction="out"/>
</method>
<method name="GetServerInformation">
<arg type="s" direction="out"/>
<arg type="s" direction="out"/>
<arg type="s" direction="out"/>
<arg type="s" direction="out"/>
</method>
<signal name="NotificationClosed">
<arg type="u"/>
<arg type="u"/>
</signal>
<signal name="ActionInvoked">
<arg type="u"/>
<arg type="s"/>
</signal>
</interface>;
const NotificationClosedReason = {
EXPIRED: 1,
@ -84,15 +87,14 @@ const rewriteRules = {
]
};
function NotificationDaemon() {
this._init();
}
const NotificationDaemon = new Lang.Class({
Name: 'NotificationDaemon',
NotificationDaemon.prototype = {
_init: function() {
DBus.session.exportObject('/org/freedesktop/Notifications', this);
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(NotificationDaemonIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/freedesktop/Notifications');
this._sources = {};
this._sources = [];
this._senderToPid = {};
this._notifications = {};
this._busProxy = new Bus();
@ -109,6 +111,14 @@ NotificationDaemon.prototype = {
_iconForNotificationData: function(icon, hints, size) {
let textureCache = St.TextureCache.get_default();
// If an icon is not specified, we use 'image-data' or 'image-path' hint for an icon
// and don't show a large image. There are currently many applications that use
// notify_notification_set_icon_from_pixbuf() from libnotify, which in turn sets
// the 'image-data' hint. These applications don't typically pass in 'app_icon'
// argument to Notify() and actually expect the pixbuf to be shown as an icon.
// So the logic here does the right thing for this case. If both an icon and either
// one of 'image-data' or 'image-path' are specified, we show both an icon and
// a large image.
if (icon) {
if (icon.substr(0, 7) == 'file://')
return textureCache.load_uri_async(icon, size, size);
@ -122,8 +132,9 @@ NotificationDaemon.prototype = {
} else if (hints['image-data']) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = hints['image-data'];
return textureCache.load_from_raw(data, hasAlpha,
width, height, rowStride, size);
return textureCache.load_from_raw(data, hasAlpha, width, height, rowStride, size);
} else if (hints['image-path']) {
return textureCache.load_uri_async(GLib.filename_to_uri(hints['image-path'], null), size, size);
} else {
let stockIcon;
switch (hints.urgency) {
@ -141,14 +152,30 @@ NotificationDaemon.prototype = {
}
},
_lookupSource: function(title, pid, trayIcon) {
for (let i = 0; i < this._sources.length; i++) {
let source = this._sources[i];
if (source.pid == pid &&
(source.initialTitle == title || source.trayIcon || trayIcon))
return source;
}
return null;
},
// Returns the source associated with ndata.notification if it is set.
// Otherwise, returns the source associated with the pid if one is
// stored in this._sources and the notification is not transient.
// Otherwise, creates a new source as long as pid is provided.
// Otherwise, returns the source associated with the title and pid if
// such source is stored in this._sources and the notification is not
// transient. If the existing or requested source is associated with
// a tray icon and passed in pid matches a pid of an existing source,
// the title match is ignored to enable representing a tray icon and
// notifications from the same application with a single source.
//
// If no existing source is found, a new source is created as long as
// pid is provided.
//
// Either a pid or ndata.notification is needed to retrieve or
// create a source.
_getSource: function(title, pid, ndata, sender) {
_getSource: function(title, pid, ndata, sender, trayIcon) {
if (!pid && !(ndata && ndata.notification))
return null;
@ -165,20 +192,24 @@ NotificationDaemon.prototype = {
// with a transient one from the same sender, so we
// always create a new source object for new transient notifications
// and never add it to this._sources .
if (!isForTransientNotification && this._sources[pid]) {
let source = this._sources[pid];
source.setTitle(title);
return source;
if (!isForTransientNotification) {
let source = this._lookupSource(title, pid, trayIcon);
if (source) {
source.setTitle(title);
return source;
}
}
let source = new Source(title, pid, sender);
let source = new Source(title, pid, sender, trayIcon);
source.setTransient(isForTransientNotification);
if (!isForTransientNotification) {
this._sources[pid] = source;
this._sources.push(source);
source.connect('destroy', Lang.bind(this,
function() {
delete this._sources[pid];
let index = this._sources.indexOf(source);
if (index >= 0)
this._sources.splice(index, 1);
}));
}
@ -186,8 +217,8 @@ NotificationDaemon.prototype = {
return source;
},
Notify: function(appName, replacesId, icon, summary, body,
actions, hints, timeout) {
NotifyAsync: function(params, invocation) {
let [appName, replacesId, icon, summary, body, actions, hints, timeout] = params;
let id;
// Filter out chat, presence, calls and invitation notifications from
@ -195,6 +226,8 @@ NotificationDaemon.prototype = {
if (appName == 'Empathy' && (hints['category'] == 'im.received' ||
hints['category'] == 'x-empathy.im.room-invitation' ||
hints['category'] == 'x-empathy.call.incoming' ||
hints['category'] == 'x-empathy.call.incoming"' ||
hints['category'] == 'x-empathy.im.subscription-request' ||
hints['category'] == 'presence.online' ||
hints['category'] == 'presence.offline')) {
// Ignore replacesId since we already sent back a
@ -204,7 +237,7 @@ NotificationDaemon.prototype = {
function () {
this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
}));
return id;
return invocation.return_value(GLib.Variant.new('(u)', [id]));
}
let rewrites = rewriteRules[appName];
@ -216,16 +249,25 @@ NotificationDaemon.prototype = {
}
}
for (let hint in hints) {
// unpack the variants
hints[hint] = hints[hint].deep_unpack();
}
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
// Be compatible with the various hints for image data
// 'image-data' is the latest name of this hint, introduced in 1.2
if (!hints['image-data']) {
// Be compatible with the various hints for image data and image path
// 'image-data' and 'image-path' are the latest name of these hints, introduced in 1.2
if (!hints['image-path'] && hints['image_path'])
hints['image-path'] = hints['image_path']; // version 1.1 of the spec
if (!hints['image-data'])
if (hints['image_data'])
hints['image-data'] = hints['image_data']; // version 1.1 of the spec
else if (hints['icon_data'])
hints['image-data'] = hints['icon_data']; // previous versions of the spec
}
else if (hints['icon_data'] && !hints['image-path'])
// early versions of the spec; 'icon_data' should only be used if 'image-path' is not available
hints['image-data'] = hints['icon_data'];
let ndata = { appName: appName,
icon: icon,
@ -243,51 +285,55 @@ NotificationDaemon.prototype = {
}
this._notifications[id] = ndata;
let sender = DBus.getCurrentMessageContext().sender;
let sender = invocation.get_sender();
let pid = this._senderToPid[sender];
let source = this._getSource(appName, pid, ndata, sender);
let source = this._getSource(appName, pid, ndata, sender, null);
if (source) {
this._notifyForSource(source, ndata);
return id;
return invocation.return_value(GLib.Variant.new('(u)', [id]));
}
if (replacesId) {
// There's already a pending call to GetConnectionUnixProcessID,
// which will see the new notification data when it finishes,
// so we don't have to do anything.
return id;
return invocation.return_value(GLib.Variant.new('(u)', [id]));;
}
this._busProxy.GetConnectionUnixProcessIDRemote(sender, Lang.bind(this,
function (pid, ex) {
// The app may have updated or removed the notification
ndata = this._notifications[id];
if (!ndata)
return;
this._busProxy.GetConnectionUnixProcessIDRemote(sender, Lang.bind(this, function (result, excp) {
// The app may have updated or removed the notification
ndata = this._notifications[id];
if (!ndata)
return;
source = this._getSource(appName, pid, ndata, sender);
if (excp) {
logError(excp, 'Call to GetConnectionUnixProcessID failed');
return;
}
// We only store sender-pid entries for persistent sources.
// Removing the entries once the source is destroyed
// would result in the entries associated with transient
// sources removed once the notification is shown anyway.
// However, keeping these pairs would mean that we would
// possibly remove an entry associated with a persistent
// source when a transient source for the same sender is
// distroyed.
if (!source.isTransient) {
this._senderToPid[sender] = pid;
source.connect('destroy', Lang.bind(this,
function() {
delete this._senderToPid[sender];
}));
}
this._notifyForSource(source, ndata);
}));
let [pid] = result;
source = this._getSource(appName, pid, ndata, sender, null);
return id;
// We only store sender-pid entries for persistent sources.
// Removing the entries once the source is destroyed
// would result in the entries associated with transient
// sources removed once the notification is shown anyway.
// However, keeping these pairs would mean that we would
// possibly remove an entry associated with a persistent
// source when a transient source for the same sender is
// distroyed.
if (!source.isTransient) {
this._senderToPid[sender] = pid;
source.connect('destroy', Lang.bind(this, function() {
delete this._senderToPid[sender];
}));
}
this._notifyForSource(source, ndata);
}));
return invocation.return_value(GLib.Variant.new('(u)', [id]));
},
_notifyForSource: function(source, ndata) {
@ -304,7 +350,7 @@ NotificationDaemon.prototype = {
ndata.notification = notification;
notification.connect('destroy', Lang.bind(this,
function(n, reason) {
delete this._notifications[id];
delete this._notifications[ndata.id];
let notificationClosedReason;
switch (reason) {
case MessageTray.NotificationDestroyedReason.EXPIRED:
@ -317,11 +363,11 @@ NotificationDaemon.prototype = {
notificationClosedReason = NotificationClosedReason.APP_CLOSED;
break;
}
this._emitNotificationClosed(id, notificationClosedReason);
this._emitNotificationClosed(ndata.id, notificationClosedReason);
}));
notification.connect('action-invoked', Lang.bind(this,
function(n, actionId) {
this._emitActionInvoked(id, actionId);
this._emitActionInvoked(ndata.id, actionId);
}));
} else {
notification.update(summary, body, { icon: iconActor,
@ -329,10 +375,35 @@ NotificationDaemon.prototype = {
clear: true });
}
// We only display a large image if an icon is also specified.
if (icon && (hints['image-data'] || hints['image-path'])) {
let image = null;
if (hints['image-data']) {
let [width, height, rowStride, hasAlpha,
bitsPerSample, nChannels, data] = hints['image-data'];
image = St.TextureCache.get_default().load_from_raw(data, hasAlpha,
width, height, rowStride, notification.IMAGE_SIZE);
} else if (hints['image-path']) {
image = St.TextureCache.get_default().load_uri_async(GLib.filename_to_uri(hints['image-path'], null),
notification.IMAGE_SIZE,
notification.IMAGE_SIZE);
}
notification.setImage(image);
} else {
notification.unsetImage();
}
if (actions.length) {
notification.setUseActionIcons(hints['action-icons'] == true);
for (let i = 0; i < actions.length - 1; i += 2)
notification.addButton(actions[i], actions[i + 1]);
for (let i = 0; i < actions.length - 1; i += 2) {
if (actions[i] == 'default')
notification.connect('clicked', Lang.bind(this,
function() {
this._emitActionInvoked(ndata.id, "default");
}));
else
notification.addButton(actions[i], actions[i + 1]);
}
}
switch (hints.urgency) {
case Urgency.LOW:
@ -392,8 +463,8 @@ NotificationDaemon.prototype = {
if (!tracker.focus_app)
return;
for (let id in this._sources) {
let source = this._sources[id];
for (let i = 0; i < this._sources.length; i++) {
let source = this._sources[i];
if (source.app == tracker.focus_app) {
source.destroyNonResidentNotifications();
return;
@ -402,68 +473,64 @@ NotificationDaemon.prototype = {
},
_emitNotificationClosed: function(id, reason) {
DBus.session.emit_signal('/org/freedesktop/Notifications',
'org.freedesktop.Notifications',
'NotificationClosed', 'uu',
[id, reason]);
this._dbusImpl.emit_signal('NotificationClosed',
GLib.Variant.new('(uu)', [id, reason]));
},
_emitActionInvoked: function(id, action) {
DBus.session.emit_signal('/org/freedesktop/Notifications',
'org.freedesktop.Notifications',
'ActionInvoked', 'us',
[id, action]);
this._dbusImpl.emit_signal('ActionInvoked',
GLib.Variant.new('(us)', [id, action]));
},
_onTrayIconAdded: function(o, icon) {
let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null, null);
source.setTrayIcon(icon);
let source = this._getSource(icon.title || icon.wm_class || _("Unknown"), icon.pid, null, null, icon);
},
_onTrayIconRemoved: function(o, icon) {
let source = this._sources[icon.pid];
let source = this._lookupSource(null, icon.pid, true);
if (source)
source.destroy();
}
};
});
DBus.conformExport(NotificationDaemon.prototype, NotificationDaemonIface);
const Source = new Lang.Class({
Name: 'NotificationDaemonSource',
Extends: MessageTray.Source,
function Source(title, pid, sender) {
this._init(title, pid, sender);
}
_init: function(title, pid, sender, trayIcon) {
this.parent(title);
Source.prototype = {
__proto__: MessageTray.Source.prototype,
this.initialTitle = title;
_init: function(title, pid, sender) {
MessageTray.Source.prototype._init.call(this, title);
this._pid = pid;
this.pid = pid;
if (sender)
// TODO: dbus-glib implementation of watch_name() doesnt return an id to be used for
// unwatch_name() or implement unwatch_name(), however when we move to using GDBus implementation,
// we should save the id here and call unwatch_name() with it in destroy().
// Moving to GDBus is the work in progress: https://bugzilla.gnome.org/show_bug.cgi?id=648651
// and https://bugzilla.gnome.org/show_bug.cgi?id=622921 .
DBus.session.watch_name(sender,
false,
null,
Lang.bind(this, this._onNameVanished));
this._nameWatcherId = Gio.DBus.session.watch_name(sender,
Gio.BusNameWatcherFlags.NONE,
null,
Lang.bind(this, this._onNameVanished));
else
this._nameWatcherId = 0;
this._setApp();
if (this.app)
this.title = this.app.get_name();
else
this.useNotificationIcon = true;
this._trayIcon = null;
this.trayIcon = trayIcon;
if (this.trayIcon) {
this._setSummaryIcon(this.trayIcon);
this.useNotificationIcon = false;
}
},
_onNameVanished: function() {
// Destroy the notification source when its sender is removed from DBus.
// Only do so if this.app is set to avoid removing "notify-send" sources, senders
// of which аre removed from DBus immediately.
// Sender being removed from DBus would normally result in a tray icon being removed,
// so allow the code path that handles the tray icon being removed to handle that case.
if (!this.trayIcon)
if (!this.trayIcon && this.app)
this.destroy();
},
@ -481,7 +548,7 @@ Source.prototype = {
},
handleSummaryClick: function() {
if (!this._trayIcon)
if (!this.trayIcon)
return false;
let event = Clutter.get_current_event();
@ -502,11 +569,11 @@ Source.prototype = {
let id = global.connect('notify::stage-input-mode', Lang.bind(this,
function () {
global.disconnect(id);
this._trayIcon.click(event);
this.trayIcon.click(event);
}));
Main.overview.hide();
} else {
this._trayIcon.click(event);
this.trayIcon.click(event);
}
return true;
},
@ -515,31 +582,25 @@ Source.prototype = {
if (this.app)
return;
this.app = Shell.WindowTracker.get_default().get_app_from_pid(this._pid);
this.app = Shell.WindowTracker.get_default().get_app_from_pid(this.pid);
if (!this.app)
return;
// Only override the icon if we were previously using
// notification-based icons (ie, not a trayicon) or if it was unset before
if (!this._trayIcon) {
if (!this.trayIcon) {
this.useNotificationIcon = false;
this._setSummaryIcon(this.app.create_icon_texture (this.ICON_SIZE));
}
},
setTrayIcon: function(icon) {
this._setSummaryIcon(icon);
this.useNotificationIcon = false;
this._trayIcon = icon;
},
open: function(notification) {
this.destroyNonResidentNotifications();
this.openApp();
},
_lastNotificationRemoved: function() {
if (!this._trayIcon)
if (!this.trayIcon)
this.destroy();
},
@ -555,6 +616,11 @@ Source.prototype = {
},
destroy: function() {
MessageTray.Source.prototype.destroy.call(this);
if (this._nameWatcherId) {
Gio.DBus.session.unwatch_name(this._nameWatcherId);
this._nameWatcherId = 0;
}
this.parent();
}
};
});

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gtk = imports.gi.Gtk;
@ -11,6 +11,7 @@ const Shell = imports.gi.Shell;
const Gdk = imports.gi.Gdk;
const AppDisplay = imports.ui.appDisplay;
const ContactDisplay = imports.ui.contactDisplay;
const Dash = imports.ui.dash;
const DND = imports.ui.dnd;
const DocDisplay = imports.ui.docDisplay;
@ -18,9 +19,11 @@ const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
const Panel = imports.ui.panel;
const Params = imports.misc.params;
const PlaceDisplay = imports.ui.placeDisplay;
const Tweener = imports.ui.tweener;
const ViewSelector = imports.ui.viewSelector;
const Wanda = imports.ui.wanda;
const WorkspacesView = imports.ui.workspacesView;
const WorkspaceThumbnail = imports.ui.workspaceThumbnail;
@ -44,11 +47,9 @@ const SwipeScrollResult = {
CLICK: 2
};
function ShellInfo() {
this._init();
}
const ShellInfo = new Lang.Class({
Name: 'ShellInfo',
ShellInfo.prototype = {
_init: function() {
this._source = null;
this._undoCallback = null;
@ -93,17 +94,30 @@ ShellInfo.prototype = {
this._source.notify(notification);
}
};
});
function Overview() {
this._init();
}
const Overview = new Lang.Class({
Name: 'Overview',
Overview.prototype = {
_init : function() {
// The actual global.background_actor is inside global.window_group,
// which is hidden when displaying the overview, so we display a clone.
this._background = new Clutter.Clone({ source: global.background_actor });
_init : function(params) {
params = Params.parse(params, { isDummy: false });
this.isDummy = params.isDummy;
// We only have an overview in user sessions, so
// create a dummy overview in other cases
if (this.isDummy) {
this.animationInProgress = false;
this.visible = false;
return;
}
// The main BackgroundActor is inside global.window_group which is
// hidden when displaying the overview, so we create a new
// one. Instances of this class share a single CoglTexture behind the
// scenes which allows us to show the background with different
// rendering options without duplicating the texture data.
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
this._background.hide();
global.overlay_group.add_actor(this._background);
@ -166,8 +180,6 @@ Overview.prototype = {
this._lastActiveWorkspaceIndex = -1;
this._lastHoveredWindow = null;
this._needsFakePointerEvent = false;
this.workspaces = null;
},
// The members we construct that are implemented in JS might
@ -175,37 +187,63 @@ Overview.prototype = {
// signal handlers and so forth. So we create them after
// construction in this init() method.
init: function() {
this.shellInfo = new ShellInfo();
if (this.isDummy)
return;
this.viewSelector = new ViewSelector.ViewSelector();
this._group.add_actor(this.viewSelector.actor);
this._shellInfo = new ShellInfo();
this._viewSelector = new ViewSelector.ViewSelector();
this._group.add_actor(this._viewSelector.actor);
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay();
this.viewSelector.addViewTab('windows', _("Windows"), this._workspacesDisplay.actor, 'text-x-generic');
this._viewSelector.addViewTab('windows', _("Windows"), this._workspacesDisplay.actor, 'text-x-generic');
let appView = new AppDisplay.AllAppDisplay();
this.viewSelector.addViewTab('applications', _("Applications"), appView.actor, 'system-run');
this._viewSelector.addViewTab('applications', _("Applications"), appView.actor, 'system-run');
// Default search providers
this.viewSelector.addSearchProvider(new AppDisplay.AppSearchProvider());
this.viewSelector.addSearchProvider(new AppDisplay.PrefsSearchProvider());
this.viewSelector.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
this.viewSelector.addSearchProvider(new DocDisplay.DocSearchProvider());
// Wanda comes obviously first
this.addSearchProvider(new Wanda.WandaSearchProvider());
this.addSearchProvider(new AppDisplay.AppSearchProvider());
this.addSearchProvider(new AppDisplay.SettingsSearchProvider());
this.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
this.addSearchProvider(new DocDisplay.DocSearchProvider());
this.addSearchProvider(new ContactDisplay.ContactSearchProvider());
// TODO - recalculate everything when desktop size changes
this.dash = new Dash.Dash();
this._group.add_actor(this.dash.actor);
this.dash.actor.add_constraint(this.viewSelector.constrainY);
this.dash.actor.add_constraint(this.viewSelector.constrainHeight);
this._dash = new Dash.Dash();
this._group.add_actor(this._dash.actor);
this._dash.actor.add_constraint(this._viewSelector.constrainY);
this._dash.actor.add_constraint(this._viewSelector.constrainHeight);
this.dashIconSize = this._dash.iconSize;
this._dash.connect('icon-size-changed',
Lang.bind(this, function() {
this.dashIconSize = this._dash.iconSize;
}));
// Translators: this is the name of the dock/favorites area on
// the left of the overview
Main.ctrlAltTabManager.addGroup(this.dash.actor, _("Dash"), 'user-bookmarks');
Main.ctrlAltTabManager.addGroup(this._dash.actor, _("Dash"), 'user-bookmarks');
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
this._relayout();
},
addSearchProvider: function(provider) {
this._viewSelector.addSearchProvider(provider);
},
removeSearchProvider: function(provider) {
this._viewSelector.removeSearchProvider(provider);
},
setMessage: function(text, undoCallback, undoLabel) {
if (this.isDummy)
return;
this._shellInfo.setMessage(text, undoCallback, undoLabel);
},
_onDragBegin: function() {
DND.addDragMonitor(this._dragMonitor);
// Remember the workspace we started from
@ -275,6 +313,9 @@ Overview.prototype = {
},
setScrollAdjustment: function(adjustment, direction) {
if (this.isDummy)
return;
this._scrollAdjustment = adjustment;
if (this._scrollAdjustment == null)
this._scrollDirection = SwipeScrollDirection.NONE;
@ -467,15 +508,15 @@ Overview.prototype = {
// Set the dash's x position - y is handled by a constraint
let dashX;
if (rtl) {
this.dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
this._dash.actor.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST);
dashX = primary.width;
} else {
dashX = 0;
}
this.dash.actor.set_x(dashX);
this._dash.actor.set_x(dashX);
this.viewSelector.actor.set_position(viewX, viewY);
this.viewSelector.actor.set_size(viewWidth, viewHeight);
this._viewSelector.actor.set_position(viewX, viewY);
this._viewSelector.actor.set_size(viewWidth, viewHeight);
},
//// Public methods ////
@ -508,6 +549,8 @@ Overview.prototype = {
//
// Animates the overview visible and grabs mouse and keyboard input
show : function() {
if (this.isDummy)
return;
if (this._shown)
return;
// Do this manually instead of using _syncInputMode, to handle failure
@ -535,19 +578,19 @@ Overview.prototype = {
//
// If we switched to displaying the actors in the Overview rather than
// clones of them, this would obviously no longer be necessary.
//
// Disable unredirection while in the overview
Meta.disable_unredirect_for_screen(global.screen);
global.window_group.hide();
this._group.show();
this._background.show();
this._workspacesDisplay.show();
this.workspaces = this._workspacesDisplay.workspacesView;
global.overlay_group.add_actor(this.workspaces.actor);
if (!this._desktopFade.child)
this._desktopFade.child = this._getDesktopClone();
if (!this.workspaces.getActiveWorkspace().hasMaximizedWindows()) {
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) {
this._desktopFade.opacity = 255;
this._desktopFade.show();
Tweener.addTween(this._desktopFade,
@ -566,6 +609,12 @@ Overview.prototype = {
onCompleteScope: this
});
Tweener.addTween(this._background,
{ dim_factor: 0.4,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
this._coverPane.raise_top();
this._coverPane.show();
this.emit('showing');
@ -578,6 +627,9 @@ Overview.prototype = {
// will result in the overview not being hidden until hideTemporarily() is
// called.
showTemporarily: function() {
if (this.isDummy)
return;
if (this._shownTemporarily)
return;
@ -590,6 +642,9 @@ Overview.prototype = {
//
// Reverses the effect of show()
hide: function() {
if (this.isDummy)
return;
if (!this._shown)
return;
@ -608,6 +663,9 @@ Overview.prototype = {
//
// Reverses the effect of showTemporarily()
hideTemporarily: function() {
if (this.isDummy)
return;
if (!this._shownTemporarily)
return;
@ -619,6 +677,9 @@ Overview.prototype = {
},
toggle: function() {
if (this.isDummy)
return;
if (this._shown)
this.hide();
else
@ -664,7 +725,7 @@ Overview.prototype = {
this.animationInProgress = true;
this._hideInProgress = true;
if (!this.workspaces.getActiveWorkspace().hasMaximizedWindows()) {
if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) {
this._desktopFade.opacity = 0;
this._desktopFade.show();
Tweener.addTween(this._desktopFade,
@ -673,7 +734,7 @@ Overview.prototype = {
transition: 'easeOutQuad' });
}
this.workspaces.hide();
this._workspacesDisplay.zoomFromOverview();
// Make other elements fade out.
Tweener.addTween(this._group,
@ -684,6 +745,12 @@ Overview.prototype = {
onCompleteScope: this
});
Tweener.addTween(this._background,
{ dim_factor: 1.0,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
this._coverPane.raise_top();
this._coverPane.show();
this.emit('hiding');
@ -704,10 +771,10 @@ Overview.prototype = {
},
_hideDone: function() {
global.window_group.show();
// Re-enable unredirection
Meta.enable_unredirect_for_screen(global.screen);
this.workspaces.destroy();
this.workspaces = null;
global.window_group.show();
this._workspacesDisplay.hide();
@ -734,5 +801,5 @@ Overview.prototype = {
this._needsFakePointerEvent = false;
}
}
};
});
Signals.addSignalMethods(Overview.prototype);

View File

@ -1,8 +1,9 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Cairo = imports.cairo;
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Pango = imports.gi.Pango;
@ -16,37 +17,44 @@ const Layout = imports.ui.layout;
const Overview = imports.ui.overview;
const PopupMenu = imports.ui.popupMenu;
const PanelMenu = imports.ui.panelMenu;
const StatusMenu = imports.ui.statusMenu;
const DateMenu = imports.ui.dateMenu;
const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const PANEL_ICON_SIZE = 24;
const STARTUP_ANIMATION_TIME = 0.2;
const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
const SPINNER_ANIMATION_TIME = 0.2;
const STANDARD_TRAY_ICON_ORDER = ['a11y', 'display', 'keyboard', 'volume', 'bluetooth', 'network', 'battery'];
const STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION = {
const STANDARD_STATUS_AREA_ORDER = ['a11y', 'keyboard', 'volume', 'bluetooth', 'network', 'battery', 'userMenu'];
const STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION = {
'a11y': imports.ui.status.accessibility.ATIndicator,
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
'keyboard': imports.ui.status.keyboard.XKBIndicator
'keyboard': imports.ui.status.keyboard.XKBIndicator,
'userMenu': imports.ui.userMenu.UserMenuButton
};
if (Config.HAVE_BLUETOOTH)
STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION['bluetooth'] = imports.ui.status.bluetooth.Indicator;
STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION['bluetooth'] = imports.ui.status.bluetooth.Indicator;
try {
STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION['network'] = imports.ui.status.network.NMApplet;
STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION['network'] = imports.ui.status.network.NMApplet;
} catch(e) {
log('NMApplet is not supported. It is possible that your NetworkManager version is too old');
}
const GDM_STATUS_AREA_ORDER = ['a11y', 'display', 'keyboard', 'volume', 'battery', 'powerMenu'];
const GDM_STATUS_AREA_SHELL_IMPLEMENTATION = {
'a11y': imports.ui.status.accessibility.ATIndicator,
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
'keyboard': imports.ui.status.keyboard.XKBIndicator,
'powerMenu': imports.gdm.powerMenu.PowerMenuButton
};
// To make sure the panel corners blend nicely with the panel,
// we draw background and borders the same way, e.g. drawing
// them as filled shapes from the outside inwards instead of
@ -91,11 +99,9 @@ function _unpremultiply(color) {
};
function AnimatedIcon(name, size) {
this._init(name, size);
}
const AnimatedIcon = new Lang.Class({
Name: 'AnimatedIcon',
AnimatedIcon.prototype = {
_init: function(name, size) {
this.actor = new St.Bin({ visible: false });
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
@ -132,13 +138,11 @@ AnimatedIcon.prototype = {
if (this._timeoutId)
Mainloop.source_remove(this._timeoutId);
}
};
});
function TextShadower() {
this._init();
}
const TextShadower = new Lang.Class({
Name: 'TextShadower',
TextShadower.prototype = {
_init: function() {
this.actor = new Shell.GenericContainer();
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
@ -218,7 +222,7 @@ TextShadower.prototype = {
child.allocate(childBox, flags);
}
}
};
});
/**
* AppMenuButton:
@ -228,23 +232,24 @@ TextShadower.prototype = {
* this menu also handles startup notification for it. So when we
* have an active startup notification, we switch modes to display that.
*/
function AppMenuButton() {
this._init();
}
const AppMenuButton = new Lang.Class({
Name: 'AppMenuButton',
Extends: PanelMenu.Button,
AppMenuButton.prototype = {
__proto__: PanelMenu.Button.prototype,
_init: function(menuManager) {
this.parent(0.0, null, true);
_init: function() {
PanelMenu.Button.prototype._init.call(this, 0.0);
this._metaDisplay = global.screen.get_display();
this._startingApps = [];
this._menuManager = menuManager;
this._targetApp = null;
this._appMenuNotifyId = 0;
this._actionGroupNotifyId = 0;
let bin = new St.Bin({ name: 'appMenu' });
this.actor.set_child(bin);
this.actor.add_actor(bin);
this.actor.bind_property("reactive", this.actor, "can-focus", 0);
this.actor.reactive = false;
this._targetIsCurrent = false;
@ -265,10 +270,6 @@ AppMenuButton.prototype = {
this._iconBottomClip = 0;
this._quitMenu = new PopupMenu.PopupMenuItem('');
this.menu.addMenuItem(this._quitMenu);
this._quitMenu.connect('activate', Lang.bind(this, this._onQuit));
this._visible = !Main.overview.visible;
if (!this._visible)
this.actor.hide();
@ -287,8 +288,9 @@ AppMenuButton.prototype = {
this._spinner.actor.lower_bottom();
let tracker = Shell.WindowTracker.get_default();
let appSys = Shell.AppSystem.get_default();
tracker.connect('notify::focus-app', Lang.bind(this, this._sync));
tracker.connect('app-state-changed', Lang.bind(this, this._onAppStateChanged));
appSys.connect('app-state-changed', Lang.bind(this, this._onAppStateChanged));
global.window_manager.connect('switch-workspace', Lang.bind(this, this._sync));
@ -446,13 +448,7 @@ AppMenuButton.prototype = {
}
},
_onQuit: function() {
if (this._targetApp == null)
return;
this._targetApp.request_quit();
},
_onAppStateChanged: function(tracker, app) {
_onAppStateChanged: function(appSys, app) {
let state = app.state;
if (state != Shell.AppState.STARTING) {
this._startingApps = this._startingApps.filter(function(a) {
@ -502,6 +498,9 @@ AppMenuButton.prototype = {
return;
}
if (!targetApp.is_on_workspace(workspace))
return;
if (!this._targetIsCurrent) {
this.actor.reactive = true;
this._targetIsCurrent = true;
@ -513,8 +512,10 @@ AppMenuButton.prototype = {
}
if (targetApp == this._targetApp) {
if (targetApp && targetApp.get_state() != Shell.AppState.STARTING)
if (targetApp && targetApp.get_state() != Shell.AppState.STARTING) {
this.stopAnimation();
this._maybeSetMenu();
}
return;
}
@ -524,43 +525,77 @@ AppMenuButton.prototype = {
this._iconBox.hide();
this._label.setText('');
if (this._appMenuNotifyId)
this._targetApp.disconnect(this._appMenuNotifyId);
if (this._actionGroupNotifyId)
this._targetApp.disconnect(this._actionGroupNotifyId);
if (targetApp) {
this._appMenuNotifyId = targetApp.connect('notify::menu', Lang.bind(this, this._sync));
this._actionGroupNotifyId = targetApp.connect('notify::action-group', Lang.bind(this, this._sync));
} else {
this._appMenuNotifyId = 0;
this._actionGroupNotifyId = 0;
}
this._targetApp = targetApp;
let icon = targetApp.get_faded_icon(2 * PANEL_ICON_SIZE);
this._label.setText(targetApp.get_name());
// TODO - _quit() doesn't really work on apps in state STARTING yet
this._quitMenu.label.set_text(_("Quit %s").format(targetApp.get_name()));
this.setName(targetApp.get_name());
this._iconBox.set_child(icon);
this._iconBox.show();
if (targetApp.get_state() == Shell.AppState.STARTING)
this.startAnimation();
else
this._maybeSetMenu();
this.emit('changed');
},
_maybeSetMenu: function() {
let menu;
if (this._targetApp.action_group && this._targetApp.menu) {
if (this.menu instanceof PopupMenu.RemoteMenu &&
this.menu.actionGroup == this._targetApp.action_group)
return;
menu = new PopupMenu.RemoteMenu(this.actor, this._targetApp.menu, this._targetApp.action_group);
} else {
if (this.menu && !(this.menu instanceof PopupMenu.RemoteMenu))
return;
// fallback to older menu
menu = new PopupMenu.PopupMenu(this.actor, 0.0, St.Side.TOP, 0);
menu.addAction(_("Quit"), Lang.bind(this, function() {
this._targetApp.request_quit();
}));
}
this.setMenu(menu);
this._menuManager.addMenu(menu);
}
};
});
Signals.addSignalMethods(AppMenuButton.prototype);
// Activities button. Because everything else in the top bar is a
// PanelMenu.Button, it simplifies some things to make this be one too.
// We just hack it up to not actually have a menu attached to it.
function ActivitiesButton() {
this._init.apply(this, arguments);
}
ActivitiesButton.prototype = {
__proto__: PanelMenu.Button.prototype,
const ActivitiesButton = new Lang.Class({
Name: 'ActivitiesButton',
Extends: PanelMenu.Button,
_init: function() {
PanelMenu.Button.prototype._init.call(this, 0.0);
this.parent(0.0);
let container = new Shell.GenericContainer();
container.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
container.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
container.connect('allocate', Lang.bind(this, this._allocate));
this.actor.child = container;
container.connect('get-preferred-width', Lang.bind(this, this._containerGetPreferredWidth));
container.connect('get-preferred-height', Lang.bind(this, this._containerGetPreferredHeight));
container.connect('allocate', Lang.bind(this, this._containerAllocate));
this.actor.add_actor(container);
this.actor.name = 'panelActivities';
/* Translators: If there is no suitable word for "Activities"
@ -592,15 +627,15 @@ ActivitiesButton.prototype = {
this._xdndTimeOut = 0;
},
_getPreferredWidth: function(actor, forHeight, alloc) {
_containerGetPreferredWidth: function(actor, forHeight, alloc) {
[alloc.min_size, alloc.natural_size] = this._label.get_preferred_width(forHeight);
},
_getPreferredHeight: function(actor, forWidth, alloc) {
_containerGetPreferredHeight: function(actor, forWidth, alloc) {
[alloc.min_size, alloc.natural_size] = this._label.get_preferred_height(forWidth);
},
_allocate: function(actor, box, flags) {
_containerAllocate: function(actor, box, flags) {
this._label.allocate(box, flags);
// The hot corner needs to be outside any padding/alignment
@ -691,19 +726,115 @@ ActivitiesButton.prototype = {
Mainloop.source_remove(this._xdndTimeOut);
this._xdndTimeOut = 0;
}
};
});
function PanelCorner(panel, side) {
this._init(panel, side);
}
const PanelCorner = new Lang.Class({
Name: 'PanelCorner',
PanelCorner.prototype = {
_init: function(panel, side) {
this._panel = panel;
_init: function(box, side) {
this._side = side;
this._box = box;
this._box.connect('style-changed', Lang.bind(this, this._boxStyleChanged));
this.actor = new St.DrawingArea({ style_class: 'panel-corner' });
this.actor.connect('style-changed', Lang.bind(this, this._styleChanged));
this.actor.connect('repaint', Lang.bind(this, this._repaint));
this.actor.connect('style-changed', Lang.bind(this, this.relayout));
},
_findRightmostButton: function(container) {
if (!container.get_children)
return null;
let children = container.get_children();
if (!children || children.length == 0)
return null;
// Start at the back and work backward
let index = children.length - 1;
while (!children[index].visible && index >= 0)
index--;
if (index < 0)
return null;
if (!(children[index].has_style_class_name('panel-menu')) &&
!(children[index].has_style_class_name('panel-button')))
return this._findRightmostButton(children[index]);
return children[index];
},
_findLeftmostButton: function(container) {
if (!container.get_children)
return null;
let children = container.get_children();
if (!children || children.length == 0)
return null;
// Start at the front and work forward
let index = 0;
while (!children[index].visible && index < children.length)
index++;
if (index == children.length)
return null;
if (!(children[index].has_style_class_name('panel-menu')) &&
!(children[index].has_style_class_name('panel-button')))
return this._findLeftmostButton(children[index]);
return children[index];
},
_boxStyleChanged: function() {
let side = this._side;
let rtlAwareContainer = this._box instanceof St.BoxLayout;
if (rtlAwareContainer &&
this._box.get_direction() == St.TextDirection.RTL) {
if (this._side == St.Side.LEFT)
side = St.Side.RIGHT;
else if (this._side == St.Side.RIGHT)
side = St.Side.LEFT;
}
let button;
if (side == St.Side.LEFT)
button = this._findLeftmostButton(this._box);
else if (side == St.Side.RIGHT)
button = this._findRightmostButton(this._box);
if (button) {
if (this._button && this._buttonStyleChangedSignalId) {
this._button.disconnect(this._buttonStyleChangedSignalId);
this._button.style = null;
}
this._button = button;
button.connect('destroy', Lang.bind(this,
function() {
if (this._button == button) {
this._button = null;
this._buttonStyleChangedSignalId = 0;
}
}));
// Synchronize the locate button's pseudo classes with this corner
this._buttonStyleChangedSignalId = button.connect('style-changed', Lang.bind(this,
function(actor) {
let pseudoClass = button.get_style_pseudo_class();
this.actor.set_style_pseudo_class(pseudoClass);
}));
// The corner doesn't support theme transitions, so override
// the .panel-button default
button.style = 'transition-duration: 0';
}
},
_repaint: function() {
@ -766,33 +897,24 @@ PanelCorner.prototype = {
cr.restore();
},
relayout: function() {
_styleChanged: function() {
let node = this.actor.get_theme_node();
let cornerRadius = node.get_length("-panel-corner-radius");
let innerBorderWidth = node.get_length('-panel-corner-inner-border-width');
this.actor.set_size(cornerRadius,
innerBorderWidth + cornerRadius);
if (this._side == St.Side.LEFT)
this.actor.set_position(this._panel.actor.x,
this._panel.actor.y + this._panel.actor.height - innerBorderWidth);
else
this.actor.set_position(this._panel.actor.x + this._panel.actor.width - cornerRadius,
this._panel.actor.y + this._panel.actor.height - innerBorderWidth);
this.actor.set_size(cornerRadius, innerBorderWidth + cornerRadius);
this.actor.set_anchor_point(0, innerBorderWidth);
}
};
});
function Panel() {
this._init();
}
const Panel = new Lang.Class({
Name: 'Panel',
Panel.prototype = {
_init : function() {
this.actor = new St.BoxLayout({ style_class: 'menu-bar',
name: 'panel',
reactive: true });
this.actor = new Shell.GenericContainer({ name: 'panel',
reactive: true });
this.actor._delegate = this;
this._statusArea = {};
@ -804,259 +926,215 @@ Panel.prototype = {
this.actor.remove_style_class_name('in-overview');
}));
this._leftPointerBarrier = 0;
this._rightPointerBarrier = 0;
this._menus = new PopupMenu.PopupMenuManager(this);
this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
this.actor.add_actor(this._leftBox);
this._centerBox = new St.BoxLayout({ name: 'panelCenter' });
this.actor.add_actor(this._centerBox);
this._rightBox = new St.BoxLayout({ name: 'panelRight' });
this.actor.add_actor(this._rightBox);
this._leftCorner = new PanelCorner(this, St.Side.LEFT);
this._rightCorner = new PanelCorner(this, St.Side.RIGHT);
if (this.actor.get_direction() == St.TextDirection.RTL)
this._leftCorner = new PanelCorner(this._rightBox, St.Side.LEFT);
else
this._leftCorner = new PanelCorner(this._leftBox, St.Side.LEFT);
/* This box container ensures that the centerBox is positioned in the *absolute*
* center, but can be pushed aside if necessary. */
this._boxContainer = new Shell.GenericContainer();
this.actor.add(this._boxContainer, { expand: true });
this._boxContainer.add_actor(this._leftBox);
this._boxContainer.add_actor(this._centerBox);
this._boxContainer.add_actor(this._rightBox);
this._boxContainer.connect('get-preferred-width', Lang.bind(this, function(box, forHeight, alloc) {
let children = box.get_children();
for (let i = 0; i < children.length; i++) {
let [childMin, childNatural] = children[i].get_preferred_width(forHeight);
alloc.min_size += childMin;
alloc.natural_size += childNatural;
}
}));
this._boxContainer.connect('get-preferred-height', Lang.bind(this, function(box, forWidth, alloc) {
let children = box.get_children();
for (let i = 0; i < children.length; i++) {
let [childMin, childNatural] = children[i].get_preferred_height(forWidth);
if (childMin > alloc.min_size)
alloc.min_size = childMin;
if (childNatural > alloc.natural_size)
alloc.natural_size = childNatural;
}
}));
this._boxContainer.connect('allocate', Lang.bind(this, function(container, box, flags) {
let allocWidth = box.x2 - box.x1;
let allocHeight = box.y2 - box.y1;
let [leftMinWidth, leftNaturalWidth] = this._leftBox.get_preferred_width(-1);
let [centerMinWidth, centerNaturalWidth] = this._centerBox.get_preferred_width(-1);
let [rightMinWidth, rightNaturalWidth] = this._rightBox.get_preferred_width(-1);
this.actor.add_actor(this._leftCorner.actor);
let sideWidth, centerWidth;
centerWidth = centerNaturalWidth;
sideWidth = (allocWidth - centerWidth) / 2;
if (this.actor.get_direction() == St.TextDirection.RTL)
this._rightCorner = new PanelCorner(this._leftBox, St.Side.RIGHT);
else
this._rightCorner = new PanelCorner(this._rightBox, St.Side.RIGHT);
this.actor.add_actor(this._rightCorner.actor);
let childBox = new Clutter.ActorBox();
childBox.y1 = 0;
childBox.y2 = allocHeight;
if (this.actor.get_direction() == St.TextDirection.RTL) {
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
leftNaturalWidth);
childBox.x2 = allocWidth;
} else {
childBox.x1 = 0;
childBox.x2 = Math.min(Math.floor(sideWidth),
leftNaturalWidth);
}
this._leftBox.allocate(childBox, flags);
childBox.x1 = Math.ceil(sideWidth);
childBox.y1 = 0;
childBox.x2 = childBox.x1 + centerWidth;
childBox.y2 = allocHeight;
this._centerBox.allocate(childBox, flags);
childBox.y1 = 0;
childBox.y2 = allocHeight;
if (this.actor.get_direction() == St.TextDirection.RTL) {
childBox.x1 = 0;
childBox.x2 = Math.min(Math.floor(sideWidth),
rightNaturalWidth);
} else {
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
rightNaturalWidth);
childBox.x2 = allocWidth;
}
this._rightBox.allocate(childBox, flags);
}));
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate));
/* Button on the left side of the panel. */
this._activitiesButton = new ActivitiesButton();
this._activities = this._activitiesButton.actor;
this._leftBox.add(this._activities);
if (global.session_type == Shell.SessionType.USER) {
this._activitiesButton = new ActivitiesButton();
this._activities = this._activitiesButton.actor;
this._leftBox.add(this._activities);
// The activities button has a pretend menu, so as to integrate
// more cleanly with the rest of the panel
this._menus.addMenu(this._activitiesButton.menu);
// The activities button has a pretend menu, so as to integrate
// more cleanly with the rest of the panel
this._menus.addMenu(this._activitiesButton.menu);
// Synchronize the button's pseudo classes with its corner
this._activities.connect('style-changed', Lang.bind(this,
function(actor) {
let rtl = actor.get_direction() == St.TextDirection.RTL;
let corner = rtl ? this._rightCorner : this._leftCorner;
let pseudoClass = actor.get_style_pseudo_class();
corner.actor.set_style_pseudo_class(pseudoClass);
}));
this._appMenu = new AppMenuButton();
this._leftBox.add(this._appMenu.actor);
this._menus.addMenu(this._appMenu.menu);
this._appMenu = new AppMenuButton(this._menus);
this._leftBox.add(this._appMenu.actor);
}
/* center */
this._dateMenu = new DateMenu.DateMenuButton();
if (global.session_type == Shell.SessionType.USER)
this._dateMenu = new DateMenu.DateMenuButton({ showEvents: true });
else
this._dateMenu = new DateMenu.DateMenuButton({ showEvents: false });
this._centerBox.add(this._dateMenu.actor, { y_fill: true });
this._menus.addMenu(this._dateMenu.menu);
/* right */
// System status applets live in statusBox, while legacy tray icons
// live in trayBox
// The trayBox is hidden when there are no tray icons.
this._trayBox = new St.BoxLayout({ name: 'legacyTray' });
this._statusBox = new St.BoxLayout({ name: 'statusTray' });
this._trayBox.hide();
this._rightBox.add(this._trayBox);
this._rightBox.add(this._statusBox);
this._userMenu = new StatusMenu.StatusMenuButton();
this._userMenu.actor.name = 'panelStatus';
this._rightBox.add(this._userMenu.actor);
// Synchronize the buttons pseudo classes with its corner
this._userMenu.actor.connect('style-changed', Lang.bind(this,
function(actor) {
let rtl = actor.get_direction() == St.TextDirection.RTL;
let corner = rtl ? this._leftCorner : this._rightCorner;
let pseudoClass = actor.get_style_pseudo_class();
corner.actor.set_style_pseudo_class(pseudoClass);
}));
if (global.session_type == Shell.SessionType.GDM) {
this._status_area_order = GDM_STATUS_AREA_ORDER;
this._status_area_shell_implementation = GDM_STATUS_AREA_SHELL_IMPLEMENTATION;
} else {
this._status_area_order = STANDARD_STATUS_AREA_ORDER;
this._status_area_shell_implementation = STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION;
}
Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded));
Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
Main.chrome.addActor(this.actor);
Main.chrome.addActor(this._leftCorner.actor, { affectsStruts: false,
affectsInputRegion: false });
Main.chrome.addActor(this._rightCorner.actor, { affectsStruts: false,
affectsInputRegion: false });
Main.layoutManager.panelBox.add(this.actor);
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here',
{ sortGroup: CtrlAltTab.SortGroup.TOP });
},
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
this._relayout();
_getPreferredWidth: function(actor, forHeight, alloc) {
alloc.min_size = -1;
alloc.natural_size = Main.layoutManager.primaryMonitor.width;
},
_getPreferredHeight: function(actor, forWidth, alloc) {
// We don't need to implement this; it's forced by the CSS
alloc.min_size = -1;
alloc.natural_size = -1;
},
_allocate: function(actor, box, flags) {
let allocWidth = box.x2 - box.x1;
let allocHeight = box.y2 - box.y1;
let [leftMinWidth, leftNaturalWidth] = this._leftBox.get_preferred_width(-1);
let [centerMinWidth, centerNaturalWidth] = this._centerBox.get_preferred_width(-1);
let [rightMinWidth, rightNaturalWidth] = this._rightBox.get_preferred_width(-1);
let sideWidth, centerWidth;
centerWidth = centerNaturalWidth;
sideWidth = (allocWidth - centerWidth) / 2;
let childBox = new Clutter.ActorBox();
childBox.y1 = 0;
childBox.y2 = allocHeight;
if (this.actor.get_direction() == St.TextDirection.RTL) {
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
leftNaturalWidth);
childBox.x2 = allocWidth;
} else {
childBox.x1 = 0;
childBox.x2 = Math.min(Math.floor(sideWidth),
leftNaturalWidth);
}
this._leftBox.allocate(childBox, flags);
childBox.x1 = Math.ceil(sideWidth);
childBox.y1 = 0;
childBox.x2 = childBox.x1 + centerWidth;
childBox.y2 = allocHeight;
this._centerBox.allocate(childBox, flags);
childBox.y1 = 0;
childBox.y2 = allocHeight;
if (this.actor.get_direction() == St.TextDirection.RTL) {
childBox.x1 = 0;
childBox.x2 = Math.min(Math.floor(sideWidth),
rightNaturalWidth);
} else {
childBox.x1 = allocWidth - Math.min(Math.floor(sideWidth),
rightNaturalWidth);
childBox.x2 = allocWidth;
}
this._rightBox.allocate(childBox, flags);
let [cornerMinWidth, cornerWidth] = this._leftCorner.actor.get_preferred_width(-1);
let [cornerMinHeight, cornerHeight] = this._leftCorner.actor.get_preferred_width(-1);
childBox.x1 = 0;
childBox.x2 = cornerWidth;
childBox.y1 = allocHeight;
childBox.y2 = allocHeight + cornerHeight;
this._leftCorner.actor.allocate(childBox, flags);
let [cornerMinWidth, cornerWidth] = this._rightCorner.actor.get_preferred_width(-1);
let [cornerMinHeight, cornerHeight] = this._rightCorner.actor.get_preferred_width(-1);
childBox.x1 = allocWidth - cornerWidth;
childBox.x2 = allocWidth;
childBox.y1 = allocHeight;
childBox.y2 = allocHeight + cornerHeight;
this._rightCorner.actor.allocate(childBox, flags);
},
startStatusArea: function() {
for (let i = 0; i < STANDARD_TRAY_ICON_ORDER.length; i++) {
let role = STANDARD_TRAY_ICON_ORDER[i];
let constructor = STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION[role];
for (let i = 0; i < this._status_area_order.length; i++) {
let role = this._status_area_order[i];
let constructor = this._status_area_shell_implementation[role];
if (!constructor) {
// This icon is not implemented (this is a bug)
continue;
}
let indicator = new constructor();
this._statusBox.add(indicator.actor);
this._menus.addMenu(indicator.menu);
this._statusArea[role] = indicator;
this.addToStatusArea(role, indicator, i);
}
// PopupMenuManager depends on menus being added in order for
// keyboard navigation
this._menus.addMenu(this._userMenu.menu);
},
startupAnimation: function() {
let oldY = this.actor.y;
this.actor.y = oldY - this.actor.height;
Tweener.addTween(this.actor,
{ y: oldY,
time: STARTUP_ANIMATION_TIME,
transition: 'easeOutQuad'
});
let oldCornerY = this._leftCorner.actor.y;
this._leftCorner.actor.y = oldCornerY - this.actor.height;
this._rightCorner.actor.y = oldCornerY - this.actor.height;
Tweener.addTween(this._leftCorner.actor,
{ y: oldCornerY,
time: STARTUP_ANIMATION_TIME,
transition: 'easeOutQuad'
});
Tweener.addTween(this._rightCorner.actor,
{ y: oldCornerY,
time: STARTUP_ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
_relayout: function() {
let primary = Main.layoutManager.primaryMonitor;
this.actor.set_position(primary.x, primary.y);
this.actor.set_size(primary.width, -1);
if (this._leftPointerBarrier)
global.destroy_pointer_barrier(this._leftPointerBarrier);
if (this._rightPointerBarrier)
global.destroy_pointer_barrier(this._rightPointerBarrier);
this._leftPointerBarrier =
global.create_pointer_barrier(primary.x, primary.y,
primary.x, primary.y + this.actor.height,
1 /* BarrierPositiveX */);
this._rightPointerBarrier =
global.create_pointer_barrier(primary.x + primary.width, primary.y,
primary.x + primary.width, primary.y + this.actor.height,
4 /* BarrierNegativeX */);
this._leftCorner.relayout();
this._rightCorner.relayout();
},
_onTrayIconAdded: function(o, icon, role) {
icon.height = PANEL_ICON_SIZE;
if (STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION[role]) {
// This icon is legacy, and replaced by a Shell version
// Hide it
return;
}
// Figure out the index in our well-known order for this icon
let position = STANDARD_TRAY_ICON_ORDER.indexOf(role);
icon._rolePosition = position;
let children = this._trayBox.get_children();
_insertStatusItem: function(actor, position) {
let children = this._rightBox.get_children();
let i;
// Walk children backwards, until we find one that isn't
// well-known, or one where we should follow
for (i = children.length - 1; i >= 0; i--) {
let rolePosition = children[i]._rolePosition;
if (!rolePosition || position > rolePosition) {
this._trayBox.insert_actor(icon, i + 1);
if (position > rolePosition) {
this._rightBox.insert_actor(actor, i + 1);
break;
}
}
if (i == -1) {
// If we didn't find a position, we must be first
this._trayBox.insert_actor(icon, 0);
this._rightBox.insert_actor(actor, 0);
}
actor._rolePosition = position;
},
addToStatusArea: function(role, indicator, position) {
if (this._statusArea[role])
throw new Error('Extension point conflict: there is already a status indicator for role ' + role);
if (!(indicator instanceof PanelMenu.Button))
throw new TypeError('Status indicator must be an instance of PanelMenu.Button');
if (!position)
position = 0;
this._insertStatusItem(indicator.actor, position);
this._menus.addMenu(indicator.menu);
this._statusArea[role] = indicator;
let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) {
this._statusArea[role] = null;
emitter.disconnect(destroyId);
}));
return indicator;
},
_onTrayIconAdded: function(o, icon, role) {
if (this._status_area_shell_implementation[role]) {
// This icon is legacy, and replaced by a Shell version
// Hide it
return;
}
// Make sure the trayBox is shown.
this._trayBox.show();
icon.height = PANEL_ICON_SIZE;
let buttonBox = new PanelMenu.ButtonBox();
let box = buttonBox.actor;
box.add_actor(icon);
this._insertStatusItem(box, this._status_area_order.indexOf(role));
},
_onTrayIconRemoved: function(o, icon) {
if (icon.get_parent() != null)
this._trayBox.remove_actor(icon);
let box = icon.get_parent();
if (box && box._delegate instanceof PanelMenu.ButtonBox)
box.destroy();
},
};
});

View File

@ -1,36 +1,152 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St;
const Lang = imports.lang;
const PopupMenu = imports.ui.popupMenu;
const Main = imports.ui.main;
const Params = imports.misc.params;
const PopupMenu = imports.ui.popupMenu;
function Button(menuAlignment) {
this._init(menuAlignment);
}
const ButtonBox = new Lang.Class({
Name: 'ButtonBox',
Button.prototype = {
_init: function(menuAlignment) {
this.actor = new St.Bin({ style_class: 'panel-button',
reactive: true,
can_focus: true,
x_fill: true,
y_fill: false,
track_hover: true });
_init: function(params) {
params = Params.parse(params, { style_class: 'panel-button' }, true);
this.actor = new Shell.GenericContainer(params);
this.actor._delegate = this;
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate));
this.actor.connect('style-changed', Lang.bind(this, this._onStyleChanged));
this._minHPadding = this._natHPadding = 0.0;
},
_onStyleChanged: function(actor) {
let themeNode = actor.get_theme_node();
this._minHPadding = themeNode.get_length('-minimum-hpadding');
this._natHPadding = themeNode.get_length('-natural-hpadding');
},
_getPreferredWidth: function(actor, forHeight, alloc) {
let children = actor.get_children();
let child = children.length > 0 ? children[0] : null;
if (child) {
[alloc.min_size, alloc.natural_size] = child.get_preferred_width(-1);
} else {
alloc.min_size = alloc.natural_size = 0;
}
alloc.min_size += 2 * this._minHPadding;
alloc.natural_size += 2 * this._natHPadding;
},
_getPreferredHeight: function(actor, forWidth, alloc) {
let children = actor.get_children();
let child = children.length > 0 ? children[0] : null;
if (child) {
[alloc.min_size, alloc.natural_size] = child.get_preferred_height(-1);
} else {
alloc.min_size = alloc.natural_size = 0;
}
},
_allocate: function(actor, box, flags) {
let children = actor.get_children();
if (children.length == 0)
return;
let child = children[0];
let [minWidth, natWidth] = child.get_preferred_width(-1);
let [minHeight, natHeight] = child.get_preferred_height(-1);
let availWidth = box.x2 - box.x1;
let availHeight = box.y2 - box.y1;
let childBox = new Clutter.ActorBox();
if (natWidth + 2 * this._natHPadding <= availWidth) {
childBox.x1 = this._natHPadding;
childBox.x2 = availWidth - this._natHPadding;
} else {
childBox.x1 = this._minHPadding;
childBox.x2 = availWidth - this._minHPadding;
}
if (natHeight <= availHeight) {
childBox.y1 = Math.floor((availHeight - natHeight) / 2);
childBox.y2 = childBox.y1 + natHeight;
} else {
childBox.y1 = 0;
childBox.y2 = availHeight;
}
child.allocate(childBox, flags);
},
});
const Button = new Lang.Class({
Name: 'PanelMenuButton',
Extends: ButtonBox,
_init: function(menuAlignment, nameText, dontCreateMenu) {
this.parent({ reactive: true,
can_focus: true,
track_hover: true });
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
this.menu = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, 0);
this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
this.menu.actor.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress));
Main.chrome.addActor(this.menu.actor, { affectsStruts: false });
this.menu.actor.hide();
if (dontCreateMenu)
this.menu = null;
else
this.setMenu(new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, 0));
this.setName(nameText);
},
setName: function(text) {
if (text != null) {
// This is the easiest way to provide a accessible name to
// this widget. The label could be also used for other
// purposes in the future.
if (!this.label) {
this.label = new St.Label({ text: text });
this.actor.label_actor = this.label;
} else
this.label.text = text;
} else {
this.label = null;
this.actor.label_actor = null;
}
},
setMenu: function(menu) {
if (this.menu)
this.menu.destroy();
this.menu = menu;
if (this.menu) {
this.menu.actor.add_style_class_name('panel-menu');
this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
this.menu.actor.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress));
Main.uiGroup.add_actor(this.menu.actor);
this.menu.actor.hide();
}
},
_onButtonPress: function(actor, event) {
if (!this.menu)
return;
if (!this.menu.isOpen) {
// Setting the max-height won't do any good if the minimum height of the
// menu is higher then the screen; it's useful if part of the menu is
@ -44,6 +160,9 @@ Button.prototype = {
},
_onSourceKeyPress: function(actor, event) {
if (!this.menu)
return false;
let symbol = event.get_key_symbol();
if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
this.menu.toggle();
@ -79,8 +198,18 @@ Button.prototype = {
this.actor.add_style_pseudo_class('active');
else
this.actor.remove_style_pseudo_class('active');
},
destroy: function() {
this.actor._delegate = null;
this.menu.destroy();
this.actor.destroy();
this.emit('destroy');
}
};
});
Signals.addSignalMethods(Button.prototype);
/* SystemStatusButton:
*
@ -88,19 +217,18 @@ Button.prototype = {
* volume, bluetooth...), which is just a PanelMenuButton with an
* icon and a tooltip
*/
function SystemStatusButton() {
this._init.apply(this, arguments);
}
const SystemStatusButton = new Lang.Class({
Name: 'SystemStatusButton',
Extends: Button,
SystemStatusButton.prototype = {
__proto__: Button.prototype,
_init: function(iconName, tooltipText, nameText) {
this.parent(0.0, nameText);
_init: function(iconName,tooltipText) {
Button.prototype._init.call(this, 0.0);
this._iconActor = new St.Icon({ icon_name: iconName,
icon_type: St.IconType.SYMBOLIC,
style_class: 'system-status-icon' });
this.actor.set_child(this._iconActor);
this.actor.add_actor(this._iconActor);
this.actor.add_style_class_name('panel-status-button');
this.setTooltip(tooltipText);
},
@ -122,4 +250,4 @@ SystemStatusButton.prototype = {
this.tooltip = null;
}
}
};
});

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
@ -22,11 +22,9 @@ const Util = imports.misc.util;
* @iconFactory: A JavaScript callback which will create an icon texture given a size parameter
* @launch: A JavaScript callback to launch the entry
*/
function PlaceInfo(id, name, iconFactory, launch) {
this._init(id, name, iconFactory, launch);
}
const PlaceInfo = new Lang.Class({
Name: 'PlaceInfo',
PlaceInfo.prototype = {
_init: function(id, name, iconFactory, launch) {
this.id = id;
this.name = name;
@ -55,29 +53,26 @@ PlaceInfo.prototype = {
isRemovable: function() {
return false;
}
};
});
// Helper function to translate launch parameters into a GAppLaunchContext
function _makeLaunchContext(params)
{
params = Params.parse(params, { workspace: null,
timestamp: null });
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
let launchContext = global.create_app_launch_context();
if (params.workspace != null)
launchContext.set_desktop(params.workspace.index());
if (params.timestamp != null)
if (params.workspace != -1)
launchContext.set_desktop(params.workspace);
if (params.timestamp != 0)
launchContext.set_timestamp(params.timestamp);
return launchContext;
}
function PlaceDeviceInfo(mount) {
this._init(mount);
}
PlaceDeviceInfo.prototype = {
__proto__: PlaceInfo.prototype,
const PlaceDeviceInfo = new Lang.Class({
Name: 'PlaceDeviceInfo',
Extends: PlaceInfo,
_init: function(mount) {
this._mount = mount;
@ -118,18 +113,16 @@ PlaceDeviceInfo.prototype = {
this._mount.unmount_finish(res);
} catch (e) {
let message = _("Failed to unmount '%s'").format(o.get_name());
Main.overview.shellInfo.setMessage(message,
Lang.bind(this, this.remove),
_("Retry"));
Main.overview.setMessage(message,
Lang.bind(this, this.remove),
_("Retry"));
}
}
};
});
function PlacesManager() {
this._init();
}
const PlacesManager = new Lang.Class({
Name: 'PlacesManager',
PlacesManager.prototype = {
_init: function() {
this._defaultPlaces = [];
this._mounts = [];
@ -162,9 +155,12 @@ PlacesManager.prototype = {
this._connect = new PlaceInfo('special:connect', _("Connect to..."),
function (size) {
return new St.Icon({ icon_name: 'applications-internet',
icon_type: St.IconType.FULLCOLOR,
icon_size: size });
// do NOT use St.Icon here, it crashes the shell
// see wanda.js for details
return St.TextureCache.get_default().load_icon_name(null,
'applications-internet',
St.IconType.FULLCOLOR,
size);
},
function (params) {
// BUG: nautilus-connect-server doesn't have a desktop file, so we can't
@ -195,9 +191,9 @@ PlacesManager.prototype = {
this._bookmarksPath = GLib.build_filenamev([GLib.get_home_dir(), '.gtk-bookmarks']);
this._bookmarksFile = Gio.file_new_for_path(this._bookmarksPath);
let monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null);
this._monitor = this._bookmarksFile.monitor_file(Gio.FileMonitorFlags.NONE, null);
this._bookmarkTimeoutId = 0;
monitor.connect('changed', Lang.bind(this, function () {
this._monitor.connect('changed', Lang.bind(this, function () {
if (this._bookmarkTimeoutId > 0)
return;
/* Defensive event compression */
@ -360,19 +356,15 @@ PlacesManager.prototype = {
_removeById: function(sourceArray, id) {
sourceArray.splice(this._lookupIndexById(sourceArray, id), 1);
}
};
});
Signals.addSignalMethods(PlacesManager.prototype);
function PlaceSearchProvider() {
this._init();
}
PlaceSearchProvider.prototype = {
__proto__: Search.SearchProvider.prototype,
const PlaceSearchProvider = new Lang.Class({
Name: 'PlaceSearchProvider',
Extends: Search.SearchProvider,
_init: function() {
Search.SearchProvider.prototype._init.call(this, _("PLACES & DEVICES"));
this.parent(_("PLACES & DEVICES"));
},
getResultMeta: function(resultId) {
@ -434,4 +426,4 @@ PlaceSearchProvider.prototype = {
let places = previousResults.map(function (id) { return Main.placesManager.lookupPlaceById(id); });
return this._searchPlaces(places, terms);
}
};
});

View File

@ -1,5 +1,5 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*-
*
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/*
* Copyright 2010 Red Hat, Inc
*
* This program is free software; you can redistribute it and/or modify
@ -23,26 +23,25 @@
const Lang = imports.lang;
const Signals = imports.signals;
const Shell = imports.gi.Shell;
const AccountsService = imports.gi.AccountsService;
const Clutter = imports.gi.Clutter;
const St = imports.gi.St;
const Pango = imports.gi.Pango;
const Gdm = imports.gi.Gdm;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Mainloop = imports.mainloop;
const Polkit = imports.gi.Polkit;
const PolkitAgent = imports.gi.PolkitAgent;
const ModalDialog = imports.ui.modalDialog;
const ShellEntry = imports.ui.shellEntry;
function AuthenticationDialog(actionId, message, cookie, userNames) {
this._init(actionId, message, cookie, userNames);
}
AuthenticationDialog.prototype = {
__proto__: ModalDialog.ModalDialog.prototype,
const AuthenticationDialog = new Lang.Class({
Name: 'AuthenticationDialog',
Extends: ModalDialog.ModalDialog,
_init: function(actionId, message, cookie, userNames) {
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'polkit-dialog' });
this.parent({ styleClass: 'polkit-dialog' });
this.actionId = actionId;
this.message = message;
@ -87,12 +86,16 @@ AuthenticationDialog.prototype = {
if (userNames.length > 1) {
log('polkitAuthenticationAgent: Received ' + userNames.length +
' identities that can be used for authentication. Only ' +
'considering the first one.');
'considering one.');
}
let userName = userNames[0];
let userName = GLib.get_user_name();
if (userNames.indexOf(userName) < 0)
userName = 'root';
if (userNames.indexOf(userName) < 0)
userName = userNames[0];
this._user = Gdm.UserManager.ref_default().get_user(userName);
this._user = AccountsService.UserManager.get_default().get_user(userName);
let userRealName = this._user.get_real_name()
this._userLoadedId = this._user.connect('notify::is_loaded',
Lang.bind(this, this._onUserChanged));
@ -139,9 +142,11 @@ AuthenticationDialog.prototype = {
this._passwordEntry = new St.Entry({ style_class: 'polkit-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._passwordBox.add(this._passwordEntry,
{expand: true });
this.setInitialKeyFocus(this._passwordEntry);
this._passwordBox.hide();
this._errorMessageLabel = new St.Label({ style_class: 'polkit-dialog-error-label' });
@ -186,13 +191,6 @@ AuthenticationDialog.prototype = {
this._session.connect('request', Lang.bind(this, this._onSessionRequest));
this._session.connect('show-error', Lang.bind(this, this._onSessionShowError));
this._session.connect('show-info', Lang.bind(this, this._onSessionShowInfo));
// Delay focus grab to avoid ModalDialog stealing focus with
// its buttons
this.connect('opened',
Lang.bind(this, function() {
this._passwordEntry.grab_key_focus();
}));
},
startAuthentication: function() {
@ -334,15 +332,12 @@ AuthenticationDialog.prototype = {
this.close(global.get_current_time());
this._emitDone(false, true);
},
};
});
Signals.addSignalMethods(AuthenticationDialog.prototype);
function AuthenticationAgent() {
this._init();
}
const AuthenticationAgent = new Lang.Class({
Name: 'AuthenticationAgent',
AuthenticationAgent.prototype = {
_init: function() {
this._native = new Shell.PolkitAuthenticationAgent();
this._native.connect('initiate', Lang.bind(this, this._onInitiate));
@ -398,12 +393,13 @@ AuthenticationAgent.prototype = {
Lang.bind(this,
function() {
this._reallyCompleteRequest(wasDismissed);
return false;
}));
} else {
this._reallyCompleteRequest(wasDismissed);
}
}
}
});
function init() {
let agent = new AuthenticationAgent();

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gio = imports.gi.Gio;
@ -12,6 +12,7 @@ const Signals = imports.signals;
const FileUtils = imports.misc.fileUtils;
const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog;
const ShellEntry = imports.ui.shellEntry;
const Tweener = imports.ui.tweener;
const Util = imports.misc.util;
const History = imports.misc.history;
@ -29,11 +30,9 @@ const EXEC_ARG_KEY = 'exec-arg';
const DIALOG_GROW_TIME = 0.1;
function CommandCompleter() {
this._init();
}
const CommandCompleter = new Lang.Class({
Name: 'CommandCompleter',
CommandCompleter.prototype = {
_init : function() {
this._changedCount = 0;
this._paths = GLib.getenv('PATH').split(':');
@ -161,16 +160,14 @@ CommandCompleter.prototype = {
return common.substr(text.length);
return common;
}
};
});
function RunDialog() {
this._init();
}
const RunDialog = new Lang.Class({
Name: 'RunDialog',
Extends: ModalDialog.ModalDialog,
RunDialog.prototype = {
__proto__: ModalDialog.ModalDialog.prototype,
_init : function() {
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'run-dialog' });
this.parent({ styleClass: 'run-dialog' });
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
this._terminalSettings = new Gio.Settings({ schema: TERMINAL_SCHEMA });
@ -210,6 +207,7 @@ __proto__: ModalDialog.ModalDialog.prototype,
this.contentLayout.add(label, { y_align: St.Align.START });
let entry = new St.Entry({ style_class: 'run-dialog-entry' });
ShellEntry.addContextMenu(entry);
this._entryText = entry.clutter_text;
this.contentLayout.add(entry, { y_align: St.Align.START });
@ -382,8 +380,7 @@ __proto__: ModalDialog.ModalDialog.prototype,
if (this._lockdownSettings.get_boolean(DISABLE_COMMAND_LINE_KEY))
return;
ModalDialog.ModalDialog.prototype.open.call(this);
this.parent();
},
};
});
Signals.addSignalMethods(RunDialog.prototype);

View File

@ -1,6 +1,5 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const DBus = imports.dbus;
const Gio = imports.gi.Gio;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
@ -70,24 +69,21 @@ function waitLeisure() {
};
}
const PerfHelperIface = {
name: 'org.gnome.Shell.PerfHelper',
methods: [{ name: 'CreateWindow', inSignature: 'iibb', outSignature: '' },
{ name: 'WaitWindows', inSignature: '', outSignature: '' },
{ name: 'DestroyWindows', inSignature: '', outSignature: ''}]
};
const PerfHelperIface = <interface name="org.gnome.Shell.PerfHelper">
<method name="CreateWindow">
<arg type="i" direction="in" />
<arg type="i" direction="in" />
<arg type="b" direction="in" />
<arg type="b" direction="in" />
</method>
<method name="WaitWindows" />
<method name="DestroyWindows" />
</interface>;
const PerfHelper = function () {
this._init();
};
PerfHelper.prototype = {
_init: function() {
DBus.session.proxifyObject(this, 'org.gnome.Shell.PerfHelper', '/org/gnome/Shell/PerfHelper');
}
};
DBus.proxifyPrototype(PerfHelper.prototype, PerfHelperIface);
var PerfHelperProxy = Gio.DBusProxy.makeProxyWrapper(PerfHelperIface);
function PerfHelper() {
return new PerfHelperProxy(Gio.DBus.session, 'org.gnome.Shell.PerfHelper', '/org/gnome/Shell/PerfHelper');
}
let _perfHelper = null;
function _getPerfHelper() {

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
@ -23,11 +23,9 @@ const MatchType = {
MULTIPLE_PREFIX: 4
};
function SearchResultDisplay(provider) {
this._init(provider);
}
const SearchResultDisplay = new Lang.Class({
Name: 'SearchResultDisplay',
SearchResultDisplay.prototype = {
_init: function(provider) {
this.provider = provider;
this.actor = null;
@ -96,7 +94,7 @@ SearchResultDisplay.prototype = {
activateSelected: function() {
throw new Error('Not implemented');
}
};
});
/**
* SearchProvider:
@ -105,13 +103,48 @@ SearchResultDisplay.prototype = {
* to the search system, then call registerProvider()
* in SearchSystem with an instance.
*/
function SearchProvider(title) {
this._init(title);
}
const SearchProvider = new Lang.Class({
Name: 'SearchProvider',
SearchProvider.prototype = {
_init: function(title) {
this.title = title;
this.searchSystem = null;
this.searchAsync = false;
},
_asyncCancelled: function() {
},
startAsync: function() {
this.searchAsync = true;
},
tryCancelAsync: function() {
if (!this.searchAsync)
return;
this._asyncCancelled();
this.searchAsync = false;
},
/**
* addItems:
* @items: an array of result identifier strings representing
* items which match the last given search terms.
*
* This should be used for something that requires a bit more
* logic; it's designed to be an asyncronous way to add a result
* to the current search.
*/
addItems: function(items) {
if (!this.searchSystem)
throw new Error('Search provider not registered');
if (!items.length)
return;
this.tryCancelAsync();
this.searchSystem.addProviderItems(this, items);
},
/**
@ -206,14 +239,12 @@ SearchProvider.prototype = {
activateResult: function(id) {
throw new Error('Not implemented');
}
};
});
Signals.addSignalMethods(SearchProvider.prototype);
function OpenSearchSystem() {
this._init();
}
const OpenSearchSystem = new Lang.Class({
Name: 'OpenSearchSystem',
OpenSearchSystem.prototype = {
_init: function() {
this._providers = [];
global.settings.connect('changed::' + DISABLED_OPEN_SEARCH_PROVIDERS_KEY, Lang.bind(this, this._refresh));
@ -301,23 +332,30 @@ OpenSearchSystem.prototype = {
}
}));
}
}
});
Signals.addSignalMethods(OpenSearchSystem.prototype);
function SearchSystem() {
this._init();
}
const SearchSystem = new Lang.Class({
Name: 'SearchSystem',
SearchSystem.prototype = {
_init: function() {
this._providers = [];
this.reset();
},
registerProvider: function (provider) {
provider.searchSystem = this;
this._providers.push(provider);
},
unregisterProvider: function (provider) {
let index = this._providers.indexOf(provider);
if (index == -1)
return;
provider.searchSystem = null;
this._providers.splice(index, 1);
},
getProviders: function() {
return this._providers;
},
@ -331,12 +369,23 @@ SearchSystem.prototype = {
this._previousResults = [];
},
addProviderItems: function(provider, items) {
this.emit('search-updated', provider, items);
},
updateSearch: function(searchString) {
searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
if (searchString == '')
return [];
return;
let terms = searchString.split(/\s+/);
this.updateSearchResults(terms);
},
updateSearchResults: function(terms) {
if (!terms)
return;
let isSubSearch = terms.length == this._previousTerms.length;
if (isSubSearch) {
for (let i = 0; i < terms.length; i++) {
@ -349,12 +398,12 @@ SearchSystem.prototype = {
let results = [];
if (isSubSearch) {
for (let i = 0; i < this._previousResults.length; i++) {
for (let i = 0; i < this._providers.length; i++) {
let [provider, previousResults] = this._previousResults[i];
provider.tryCancelAsync();
try {
let providerResults = provider.getSubsearchResultSet(previousResults, terms);
if (providerResults.length > 0)
results.push([provider, providerResults]);
results.push([provider, providerResults]);
} catch (error) {
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
}
@ -362,10 +411,10 @@ SearchSystem.prototype = {
} else {
for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i];
provider.tryCancelAsync();
try {
let providerResults = provider.getInitialResultSet(terms);
if (providerResults.length > 0)
results.push([provider, providerResults]);
results.push([provider, providerResults]);
} catch (error) {
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
}
@ -374,8 +423,7 @@ SearchSystem.prototype = {
this._previousTerms = terms;
this._previousResults = results;
return results;
}
};
this.emit('search-completed', results);
},
});
Signals.addSignalMethods(SearchSystem.prototype);

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
@ -15,11 +15,9 @@ const Search = imports.ui.search;
const MAX_SEARCH_RESULTS_ROWS = 1;
function SearchResult(provider, metaInfo, terms) {
this._init(provider, metaInfo, terms);
}
const SearchResult = new Lang.Class({
Name: 'SearchResult',
SearchResult.prototype = {
_init: function(provider, metaInfo, terms) {
this.provider = provider;
this.metaInfo = metaInfo;
@ -88,7 +86,7 @@ SearchResult.prototype = {
},
getDragActor: function(stageX, stageY) {
return this.metaInfo['createIcon'](Main.overview.dash.iconSize);
return this.metaInfo['createIcon'](Main.overview.dashIconSize);
},
shellWorkspaceLaunch: function(params) {
@ -97,20 +95,18 @@ SearchResult.prototype = {
else
this.provider.activateResult(this.metaInfo.id, params);
}
};
});
function GridSearchResults(provider) {
this._init(provider);
}
const GridSearchResults = new Lang.Class({
Name: 'GridSearchResults',
Extends: Search.SearchResultDisplay,
GridSearchResults.prototype = {
__proto__: Search.SearchResultDisplay.prototype,
_init: function(provider, grid) {
this.parent(provider);
_init: function(provider) {
Search.SearchResultDisplay.prototype._init.call(this, provider);
this._grid = new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START });
this._grid = grid || new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START });
this.actor = new St.Bin({ x_align: St.Align.START });
this.actor.set_child(this._grid.actor);
@ -179,16 +175,15 @@ GridSearchResults.prototype = {
let targetActor = this._grid.getItemAtIndex(this.selectionIndex);
targetActor._delegate.activate();
}
};
});
const SearchResults = new Lang.Class({
Name: 'SearchResults',
function SearchResults(searchSystem, openSearchSystem) {
this._init(searchSystem, openSearchSystem);
}
SearchResults.prototype = {
_init: function(searchSystem, openSearchSystem) {
this._searchSystem = searchSystem;
this._searchSystem.connect('search-updated', Lang.bind(this, this._updateCurrentResults));
this._searchSystem.connect('search-completed', Lang.bind(this, this._updateResults));
this._openSearchSystem = openSearchSystem;
this.actor = new St.BoxLayout({ name: 'searchResults',
@ -223,9 +218,11 @@ SearchResults.prototype = {
this._selectedProvider = -1;
this._providers = this._searchSystem.getProviders();
this._providerMeta = [];
for (let i = 0; i < this._providers.length; i++)
this._providerMetaResults = {};
for (let i = 0; i < this._providers.length; i++) {
this.createProviderMeta(this._providers[i]);
this._providerMetaResults[this.providers[i].title] = [];
}
this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' });
this.actor.add(this._searchProvidersBox);
@ -290,11 +287,23 @@ SearchResults.prototype = {
}
resultDisplayBin.set_child(resultDisplay.actor);
this._providerMeta.push({ actor: providerBox,
this._providerMeta.push({ provider: provider,
actor: providerBox,
resultDisplay: resultDisplay });
this._content.add(providerBox);
},
destroyProviderMeta: function(provider) {
for (let i=0; i < this._providerMeta.length; i++) {
let meta = this._providerMeta[i];
if (meta.provider == provider) {
meta.actor.destroy();
this._providerMeta.splice(i, 1);
break;
}
}
},
_clearDisplay: function() {
this._selectedProvider = -1;
this._visibleResultsCount = 0;
@ -305,6 +314,12 @@ SearchResults.prototype = {
}
},
_clearDisplayForProvider: function(index) {
let meta = this._providerMeta[index];
meta.resultDisplay.clear();
meta.actor.hide();
},
reset: function() {
this._searchSystem.reset();
this._statusText.hide();
@ -319,15 +334,24 @@ SearchResults.prototype = {
this._statusText.show();
},
doSearch: function (searchString) {
this._searchSystem.updateSearch(searchString);
},
_metaForProvider: function(provider) {
return this._providerMeta[this._providers.indexOf(provider)];
},
updateSearch: function (searchString) {
let results = this._searchSystem.updateSearch(searchString);
this._clearDisplay();
_updateCurrentResults: function(searchSystem, provider, results) {
let terms = searchSystem.getTerms();
let meta = this._metaForProvider(provider);
meta.resultDisplay.clear();
meta.actor.show();
meta.resultDisplay.renderResults(results, terms);
return true;
},
_updateResults: function(searchSystem, results) {
if (results.length == 0) {
this._statusText.set_text(_("No matching results."));
this._statusText.show();
@ -337,7 +361,7 @@ SearchResults.prototype = {
this._statusText.hide();
}
let terms = this._searchSystem.getTerms();
let terms = searchSystem.getTerms();
this._openSearchSystem.setSearchTerms(terms);
// To avoid CSS transitions causing flickering
@ -349,9 +373,15 @@ SearchResults.prototype = {
for (let i = 0; i < results.length; i++) {
let [provider, providerResults] = results[i];
let meta = this._metaForProvider(provider);
meta.actor.show();
meta.resultDisplay.renderResults(providerResults, terms);
if (providerResults.length == 0) {
this._clearDisplayForProvider(i);
} else {
this._providerMetaResults[provider.title] = providerResults;
this._clearDisplayForProvider(i);
let meta = this._metaForProvider(provider);
meta.actor.show();
meta.resultDisplay.renderResults(providerResults, terms);
}
}
if (this._selectedOpenSearchButton == -1)
@ -449,4 +479,4 @@ SearchResults.prototype = {
resultDisplay.activateSelected();
Main.overview.hide();
}
};
});

View File

@ -1,29 +1,88 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const DBus = imports.dbus;
const Lang = imports.lang;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Shell = imports.gi.Shell;
const Config = imports.misc.config;
const ExtensionSystem = imports.ui.extensionSystem;
const ExtensionUtils = imports.misc.extensionUtils;
const Flashspot = imports.ui.flashspot;
const Main = imports.ui.main;
const GnomeShellIface = {
name: 'org.gnome.Shell',
methods: [{ name: 'Eval',
inSignature: 's',
outSignature: 'bs'
}
],
signals: [],
properties: [{ name: 'OverviewActive',
signature: 'b',
access: 'readwrite' }]
};
const GnomeShellIface = <interface name="org.gnome.Shell">
<method name="Eval">
<arg type="s" direction="in" name="script" />
<arg type="b" direction="out" name="success" />
<arg type="s" direction="out" name="result" />
</method>
<method name="ListExtensions">
<arg type="a{sa{sv}}" direction="out" name="extensions" />
</method>
<method name="GetExtensionInfo">
<arg type="s" direction="in" name="extension" />
<arg type="a{sv}" direction="out" name="info" />
</method>
<method name="GetExtensionErrors">
<arg type="s" direction="in" name="extension" />
<arg type="as" direction="out" name="errors" />
</method>
<method name="ScreenshotArea">
<arg type="i" direction="in" name="x"/>
<arg type="i" direction="in" name="y"/>
<arg type="i" direction="in" name="width"/>
<arg type="i" direction="in" name="height"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
</method>
<method name="ScreenshotWindow">
<arg type="b" direction="in" name="include_frame"/>
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
</method>
<method name="Screenshot">
<arg type="b" direction="in" name="flash"/>
<arg type="s" direction="in" name="filename"/>
<arg type="b" direction="out" name="success"/>
</method>
<method name="EnableExtension">
<arg type="s" direction="in" name="uuid"/>
</method>
<method name="DisableExtension">
<arg type="s" direction="in" name="uuid"/>
</method>
<method name="InstallRemoteExtension">
<arg type="s" direction="in" name="uuid"/>
<arg type="s" direction="in" name="version"/>
</method>
<method name="UninstallExtension">
<arg type="s" direction="in" name="uuid"/>
<arg type="b" direction="out" name="success"/>
</method>
<method name="LaunchExtensionPrefs">
<arg type="s" direction="in" name="uuid"/>
</method>
<property name="OverviewActive" type="b" access="readwrite" />
<property name="ApiVersion" type="i" access="read" />
<property name="ShellVersion" type="s" access="read" />
<signal name="ExtensionStatusChanged">
<arg type="s" name="uuid"/>
<arg type="i" name="state"/>
<arg type="s" name="error"/>
</signal>
</interface>;
function GnomeShell() {
this._init();
}
const GnomeShell = new Lang.Class({
Name: 'GnomeShellDBus',
GnomeShell.prototype = {
_init: function() {
DBus.session.exportObject('/org/gnome/Shell', this);
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(GnomeShellIface, this);
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell');
ExtensionSystem.connect('extension-state-changed',
Lang.bind(this, this._extensionStateChanged));
},
/**
@ -41,6 +100,9 @@ GnomeShell.prototype = {
*
*/
Eval: function(code) {
if (!global.settings.get_boolean('development-tools'))
return [false, null];
let returnValue;
let success;
try {
@ -56,6 +118,156 @@ GnomeShell.prototype = {
return [success, returnValue];
},
_onScreenshotComplete: function(obj, result, area, flash, invocation) {
if (flash) {
let flashspot = new Flashspot.Flashspot(area);
flashspot.fire();
}
let retval = GLib.Variant.new('(b)', [result]);
invocation.return_value(retval);
},
/**
* ScreenshotArea:
* @x: The X coordinate of the area
* @y: The Y coordinate of the area
* @width: The width of the area
* @height: The height of the area
* @filename: The filename for the screenshot
*
* Takes a screenshot of the passed in area and saves it
* in @filename as png image, it returns a boolean
* indicating whether the operation was successful or not.
*
*/
ScreenshotAreaAsync : function (params, invocation) {
let [x, y, width, height, flash, filename, callback] = params;
global.screenshot_area (x, y, width, height, filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
},
/**
* ScreenshotWindow:
* @include_frame: Whether to include the frame or not
* @filename: The filename for the screenshot
*
* Takes a screenshot of the focused window (optionally omitting the frame)
* and saves it in @filename as png image, it returns a boolean
* indicating whether the operation was successful or not.
*
*/
ScreenshotWindowAsync : function (params, invocation) {
let [include_frame, flash, filename] = params;
global.screenshot_window (include_frame, filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
},
/**
* Screenshot:
* @filename: The filename for the screenshot
*
* Takes a screenshot of the whole screen and saves it
* in @filename as png image, it returns a boolean
* indicating whether the operation was successful or not.
*
*/
ScreenshotAsync : function (params, invocation) {
let [flash, filename] = params;
global.screenshot(filename,
Lang.bind(this, this._onScreenshotComplete,
flash, invocation));
},
ListExtensions: function() {
let out = {};
for (let uuid in ExtensionUtils.extensions) {
let dbusObj = this.GetExtensionInfo(uuid);
out[uuid] = dbusObj;
}
return out;
},
GetExtensionInfo: function(uuid) {
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return {};
let obj = {};
Lang.copyProperties(extension.metadata, obj);
// Only serialize the properties that we actually need.
const serializedProperties = ["type", "state", "path", "error", "hasPrefs"];
serializedProperties.forEach(function(prop) {
obj[prop] = extension[prop];
});
let out = {};
for (let key in obj) {
let val = obj[key];
let type;
switch (typeof val) {
case 'string':
type = 's';
break;
case 'number':
type = 'd';
break;
case 'boolean':
type = 'b';
break;
default:
continue;
}
out[key] = GLib.Variant.new(type, val);
}
return out;
},
GetExtensionErrors: function(uuid) {
let extension = ExtensionUtils.extensions[uuid];
if (!extension)
return [];
if (!extension.errors)
return [];
return extension.errors;
},
EnableExtension: function(uuid) {
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);
},
DisableExtension: function(uuid) {
let enabledExtensions = global.settings.get_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY);
while (enabledExtensions.indexOf(uuid) != -1)
enabledExtensions.splice(enabledExtensions.indexOf(uuid), 1);
global.settings.set_strv(ExtensionSystem.ENABLED_EXTENSIONS_KEY, enabledExtensions);
},
InstallRemoteExtension: function(uuid, version_tag) {
ExtensionSystem.installExtensionFromUUID(uuid, version_tag);
},
UninstallExtension: function(uuid) {
return ExtensionSystem.uninstallExtensionFromUUID(uuid);
},
LaunchExtensionPrefs: function(uuid) {
let appSys = Shell.AppSystem.get_default();
let app = appSys.lookup_app('gnome-shell-extension-prefs.desktop');
app.launch(global.display.get_current_time_roundtrip(),
['extension:///' + uuid], -1, null);
},
get OverviewActive() {
return Main.overview.visible;
},
@ -65,8 +277,14 @@ GnomeShell.prototype = {
Main.overview.show();
else
Main.overview.hide();
},
ApiVersion: ExtensionSystem.API_VERSION,
ShellVersion: Config.PACKAGE_VERSION,
_extensionStateChanged: function(_, newState) {
this._dbusImpl.emit_signal('ExtensionStatusChanged',
GLib.Variant.new('(sis)', [newState.uuid, newState.state, newState.error]));
}
};
DBus.conformExport(GnomeShell.prototype, GnomeShellIface);
});

168
js/ui/shellEntry.js Normal file
View File

@ -0,0 +1,168 @@
const Clutter = imports.gi.Clutter;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const St = imports.gi.St;
const Main = imports.ui.main;
const Params = imports.misc.params;
const PopupMenu = imports.ui.popupMenu;
const _EntryMenu = new Lang.Class({
Name: 'ShellEntryMenu',
Extends: PopupMenu.PopupMenu,
_init: function(entry, params) {
params = Params.parse (params, { isPassword: false });
this.parent(entry, 0, St.Side.TOP);
this.actor.add_style_class_name('entry-context-menu');
this._entry = entry;
this._clipboard = St.Clipboard.get_default();
// Populate menu
let item;
item = new PopupMenu.PopupMenuItem(_("Copy"));
item.connect('activate', Lang.bind(this, this._onCopyActivated));
this.addMenuItem(item);
this._copyItem = item;
item = new PopupMenu.PopupMenuItem(_("Paste"));
item.connect('activate', Lang.bind(this, this._onPasteActivated));
this.addMenuItem(item);
this._pasteItem = item;
this._passwordItem = null;
if (params.isPassword) {
item = new PopupMenu.PopupMenuItem('');
item.connect('activate', Lang.bind(this,
this._onPasswordActivated));
this.addMenuItem(item);
this._passwordItem = item;
}
Main.uiGroup.add_actor(this.actor);
this.actor.hide();
},
open: function() {
this._updatePasteItem();
this._updateCopyItem();
if (this._passwordItem)
this._updatePasswordItem();
let direction = Gtk.DirectionType.TAB_FORWARD;
if (!this.actor.navigate_focus(null, direction, false))
this.actor.grab_key_focus();
this.parent();
},
_updateCopyItem: function() {
let selection = this._entry.clutter_text.get_selection();
this._copyItem.setSensitive(selection && selection != '');
},
_updatePasteItem: function() {
this._clipboard.get_text(Lang.bind(this,
function(clipboard, text) {
this._pasteItem.setSensitive(text && text != '');
}));
},
_updatePasswordItem: function() {
let textHidden = (this._entry.clutter_text.password_char);
if (textHidden)
this._passwordItem.label.set_text(_("Show Text"));
else
this._passwordItem.label.set_text(_("Hide Text"));
},
_onCopyActivated: function() {
let selection = this._entry.clutter_text.get_selection();
this._clipboard.set_text(selection);
},
_onPasteActivated: function() {
this._clipboard.get_text(Lang.bind(this,
function(clipboard, text) {
if (!text)
return;
this._entry.clutter_text.delete_selection();
let pos = this._entry.clutter_text.get_cursor_position();
this._entry.clutter_text.insert_text(text, pos);
}));
},
_onPasswordActivated: function() {
let visible = !!(this._entry.clutter_text.password_char);
this._entry.clutter_text.set_password_char(visible ? '' : '\u25cf');
}
});
function _setMenuAlignment(entry, stageX) {
let [success, entryX, entryY] = entry.transform_stage_point(stageX, 0);
if (success)
entry._menu.setSourceAlignment(entryX / entry.width);
};
function _onClicked(action, actor) {
let entry = actor._menu ? actor : actor.get_parent();
if (entry._menu.isOpen) {
entry._menu.close();
} else if (action.get_button() == 3) {
let [stageX, stageY] = action.get_coords();
_setMenuAlignment(entry, stageX);
entry._menu.open();
}
};
function _onLongPress(action, actor, state) {
let entry = actor._menu ? actor : actor.get_parent();
if (state == Clutter.LongPressState.QUERY)
return action.get_button() == 1 && !entry._menu.isOpen;
if (state == Clutter.LongPressState.ACTIVATE) {
let [stageX, stageY] = action.get_coords();
_setMenuAlignment(entry, stageX);
entry._menu.open();
}
return false;
};
function _onPopup(actor) {
let entry = actor._menu ? actor : actor.get_parent();
let [success, textX, textY, lineHeight] = entry.clutter_text.position_to_coords(-1);
if (success)
entry._menu.setSourceAlignment(textX / entry.width);
entry._menu.open();
};
function addContextMenu(entry, params) {
if (entry._menu)
return;
entry._menu = new _EntryMenu(entry, params);
entry._menuManager = new PopupMenu.PopupMenuManager({ actor: entry });
entry._menuManager.addMenu(entry._menu);
let clickAction;
// Add a click action to both the entry and its clutter_text; the former
// so padding is included in the clickable area, the latter because the
// event processing of ClutterText prevents event-bubbling.
clickAction = new Clutter.ClickAction();
clickAction.connect('clicked', _onClicked);
clickAction.connect('long-press', _onLongPress);
entry.clutter_text.add_action(clickAction);
clickAction = new Clutter.ClickAction();
clickAction.connect('clicked', _onClicked);
clickAction.connect('long-press', _onLongPress);
entry.add_action(clickAction);
entry.connect('popup-menu', _onPopup);
}

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Signals = imports.signals;
@ -50,11 +50,9 @@ function _setLabelsForMessage(dialog, message) {
/* -------------------------------------------------------- */
function ListItem(app) {
this._init(app);
}
const ListItem = new Lang.Class({
Name: 'ListItem',
ListItem.prototype = {
_init: function(app) {
this._app = app;
@ -84,16 +82,14 @@ ListItem.prototype = {
_onClicked: function() {
this.emit('activate');
this._app.activate(-1);
this._app.activate();
}
};
});
Signals.addSignalMethods(ListItem.prototype);
function ShellMountOperation(source, params) {
this._init(source, params);
}
const ShellMountOperation = new Lang.Class({
Name: 'ShellMountOperation',
ShellMountOperation.prototype = {
_init: function(source, params) {
params = Params.parse(params, { reaskPassword: false });
@ -190,17 +186,14 @@ ShellMountOperation.prototype = {
this._processesDialog.update(message, processes, choices);
},
}
});
function ShellMountQuestionDialog(icon) {
this._init(icon);
}
ShellMountQuestionDialog.prototype = {
__proto__: ModalDialog.ModalDialog.prototype,
const ShellMountQuestionDialog = new Lang.Class({
Name: 'ShellMountQuestionDialog',
Extends: ModalDialog.ModalDialog,
_init: function(icon) {
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'mount-question-dialog' });
this.parent({ styleClass: 'mount-question-dialog' });
let mainContentLayout = new St.BoxLayout();
this.contentLayout.add(mainContentLayout, { x_fill: true,
@ -218,6 +211,8 @@ ShellMountQuestionDialog.prototype = {
{ y_align: St.Align.START });
this.subjectLabel = new St.Label({ style_class: 'mount-question-dialog-subject' });
this.subjectLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
this.subjectLabel.clutter_text.line_wrap = true;
messageLayout.add(this.subjectLabel,
{ y_fill: false,
@ -236,19 +231,16 @@ ShellMountQuestionDialog.prototype = {
_setLabelsForMessage(this, message);
_setButtonsForChoices(this, choices);
}
}
});
Signals.addSignalMethods(ShellMountQuestionDialog.prototype);
function ShellMountPasswordSource(message, icon, reaskPassword) {
this._init(message, icon, reaskPassword);
}
ShellMountPasswordSource.prototype = {
__proto__: MessageTray.Source.prototype,
const ShellMountPasswordSource = new Lang.Class({
Name: 'ShellMountPasswordSource',
Extends: MessageTray.Source,
_init: function(message, icon, reaskPassword) {
let strings = message.split('\n');
MessageTray.Source.prototype._init.call(this, strings[0]);
this.parent(strings[0]);
this._notification = new ShellMountPasswordNotification(this, strings, icon, reaskPassword);
@ -256,21 +248,15 @@ ShellMountPasswordSource.prototype = {
Main.messageTray.add(this);
this.notify(this._notification);
},
}
});
Signals.addSignalMethods(ShellMountPasswordSource.prototype);
function ShellMountPasswordNotification(source, strings, icon, reaskPassword) {
this._init(source, strings, icon, reaskPassword);
}
ShellMountPasswordNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
const ShellMountPasswordNotification = new Lang.Class({
Name: 'ShellMountPasswordNotification',
Extends: MessageTray.Notification,
_init: function(source, strings, icon, reaskPassword) {
MessageTray.Notification.prototype._init.call(this, source,
strings[0], null,
{ customContent: true,
icon: icon });
this.parent(source, strings[0], null, { customContent: true, icon: icon });
// set the notification to transient and urgent, so that it
// expands out
@ -305,17 +291,14 @@ ShellMountPasswordNotification.prototype = {
this.source.emit('password-ready', text);
}
}
});
function ShellProcessesDialog(icon) {
this._init(icon);
}
ShellProcessesDialog.prototype = {
__proto__: ModalDialog.ModalDialog.prototype,
const ShellProcessesDialog = new Lang.Class({
Name: 'ShellProcessesDialog',
Extends: ModalDialog.ModalDialog,
_init: function(icon) {
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'show-processes-dialog' });
this.parent({ styleClass: 'show-processes-dialog' });
let mainContentLayout = new St.BoxLayout();
this.contentLayout.add(mainContentLayout, { x_fill: true,
@ -401,5 +384,5 @@ ShellProcessesDialog.prototype = {
_setLabelsForMessage(this, message);
_setButtonsForChoices(this, choices);
}
}
Signals.addSignalMethods(ShellProcessesDialog.prototype);
});
Signals.addSignalMethods(ShellProcessesDialog.prototype);

View File

@ -1,7 +1,6 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const DBus = imports.dbus;
const GConf = imports.gi.GConf;
const GDesktopEnums = imports.gi.GDesktopEnums;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
@ -30,8 +29,8 @@ const DPI_FACTOR_LARGE = 1.25;
const DPI_FACTOR_LARGER = 1.5;
const DPI_FACTOR_LARGEST = 2.0;
const KEY_META_DIR = '/apps/metacity/general';
const KEY_VISUAL_BELL = KEY_META_DIR + '/visual_bell';
const WM_SCHEMA = 'org.gnome.desktop.wm.preferences';
const KEY_VISUAL_BELL = 'visual-bell';
const DESKTOP_INTERFACE_SCHEMA = 'org.gnome.desktop.interface';
const KEY_GTK_THEME = 'gtk-theme';
@ -40,19 +39,12 @@ const KEY_TEXT_SCALING_FACTOR = 'text-scaling-factor';
const HIGH_CONTRAST_THEME = 'HighContrast';
function ATIndicator() {
this._init.apply(this, arguments);
}
ATIndicator.prototype = {
__proto__: PanelMenu.SystemStatusButton.prototype,
const ATIndicator = new Lang.Class({
Name: 'ATIndicator',
Extends: PanelMenu.SystemStatusButton,
_init: function() {
PanelMenu.SystemStatusButton.prototype._init.call(this, 'preferences-desktop-accessibility', null);
let client = GConf.Client.get_default();
client.add_dir(KEY_META_DIR, GConf.ClientPreloadType.PRELOAD_ONELEVEL, null);
client.notify_add(KEY_META_DIR, Lang.bind(this, this._keyChanged), null, null);
this.parent('preferences-desktop-accessibility', null, _("Accessibility"));
let highContrast = this._buildHCItem();
this.menu.addMenuItem(highContrast);
@ -68,11 +60,11 @@ ATIndicator.prototype = {
// 'screen-reader-enabled');
// this.menu.addMenuItem(screenReader);
// let screenKeyboard = this._buildItem(_("Screen Keyboard"), APPLICATIONS_SCHEMA,
// 'screen-keyboard-enabled');
// this.menu.addMenuItem(screenKeyboard);
let screenKeyboard = this._buildItem(_("Screen Keyboard"), APPLICATIONS_SCHEMA,
'screen-keyboard-enabled');
this.menu.addMenuItem(screenKeyboard);
let visualBell = this._buildItemGConf(_("Visual Alerts"), client, KEY_VISUAL_BELL);
let visualBell = this._buildItem(_("Visual Alerts"), WM_SCHEMA, KEY_VISUAL_BELL);
this.menu.addMenuItem(visualBell);
let stickyKeys = this._buildItem(_("Sticky Keys"), A11Y_SCHEMA, KEY_STICKY_KEYS_ENABLED);
@ -88,11 +80,7 @@ ATIndicator.prototype = {
this.menu.addMenuItem(mouseKeys);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addAction(_("Universal Access Settings"), function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().get_app('gnome-universal-access-panel.desktop');
app.activate(-1);
});
this.menu.addSettingsAction(_("Universal Access Settings"), 'gnome-universal-access-panel.desktop');
},
_buildItemExtended: function(string, initial_value, writable, on_set) {
@ -106,22 +94,6 @@ ATIndicator.prototype = {
return widget;
},
_buildItemGConf: function(string, client, key) {
function on_get() {
return client.get_bool(key);
}
let widget = this._buildItemExtended(string,
client.get_bool(key),
client.key_is_writable(key),
function(enabled) {
client.set_bool(key, enabled);
});
this.connect('gconf-changed', function() {
widget.setToggleState(client.get_bool(key));
});
return widget;
},
_buildItem: function(string, schema, key) {
let settings = new Gio.Settings({ schema: schema });
let widget = this._buildItemExtended(string,
@ -195,10 +167,5 @@ ATIndicator.prototype = {
widget.setToggleState(active);
});
return widget;
},
_keyChanged: function() {
this.emit('gconf-changed');
}
};
Signals.addSignalMethods(ATIndicator.prototype);
});

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Gdk = imports.gi.Gdk;
@ -23,17 +23,13 @@ const ConnectionState = {
CONNECTING: 3
}
function Indicator() {
this._init.apply(this, arguments);
}
Indicator.prototype = {
__proto__: PanelMenu.SystemStatusButton.prototype,
const Indicator = new Lang.Class({
Name: 'BTIndicator',
Extends: PanelMenu.SystemStatusButton,
_init: function() {
PanelMenu.SystemStatusButton.prototype._init.call(this, 'bluetooth-disabled', null);
this.parent('bluetooth-disabled', null, _("Bluetooth"));
GLib.spawn_command_line_sync ('pkill -f "^bluetooth-applet$"');
this._applet = new GnomeBluetoothApplet.Applet();
this._killswitch = new PopupMenu.PopupSwitchMenuItem(_("Bluetooth"), false);
@ -67,7 +63,6 @@ Indicator.prototype = {
new PopupMenu.PopupMenuItem(_("Set up a New Device...")),
new PopupMenu.PopupSeparatorMenuItem()];
this._hasDevices = false;
this._deviceSep = this._fullMenuItems[0]; // hidden if no device exists
this._fullMenuItems[1].connect('activate', function() {
GLib.spawn_command_line_async('bluetooth-sendto');
@ -89,11 +84,7 @@ Indicator.prototype = {
this._applet.connect('notify::show-full-menu', Lang.bind(this, this._updateFullMenu));
this._updateFullMenu();
this.menu.addAction(_("Bluetooth Settings"), function() {
Main.overview.hide()
let app = Shell.AppSystem.get_default().get_app('bluetooth-properties.desktop');
app.activate(-1);
});
this.menu.addSettingsAction(_("Bluetooth Settings"), 'bluetooth-properties.desktop');
this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
@ -162,10 +153,6 @@ Indicator.prototype = {
this._hasDevices = true;
}
}
if (this._hasDevices)
this._deviceSep.actor.show();
else
this._deviceSep.actor.hide();
},
_updateDeviceItem: function(item, device) {
@ -213,9 +200,10 @@ Indicator.prototype = {
_buildDeviceSubMenu: function(item, device) {
if (device.can_connect) {
let menuitem = new PopupMenu.PopupSwitchMenuItem(_("Connection"), device.connected);
item._connected = device.connected;
item._connectedMenuitem = new PopupMenu.PopupSwitchMenuItem(_("Connection"), device.connected);
item._connectedMenuitem.connect('toggled', Lang.bind(this, function() {
item._connectedMenuItem = menuitem;
menuitem.connect('toggled', Lang.bind(this, function() {
if (item._connected > ConnectionState.CONNECTED) {
// operation already in progress, revert
// (should not happen anyway)
@ -250,7 +238,7 @@ Indicator.prototype = {
}
}));
item.menu.addMenuItem(item._connectedMenuitem);
item.menu.addMenuItem(menuitem);
}
if (device.capabilities & GnomeBluetoothApplet.Capabilities.OBEX_PUSH) {
@ -277,21 +265,15 @@ Indicator.prototype = {
switch (device.type) {
case GnomeBluetoothApplet.Type.KEYBOARD:
item.menu.addAction(_("Keyboard Settings"), function() {
GLib.spawn_command_line_async('gnome-control-center keyboard');
});
item.menu.addSettingsAction(_("Keyboard Settings"), 'gnome-keyboard-panel.desktop');
break;
case GnomeBluetoothApplet.Type.MOUSE:
item.menu.addAction(_("Mouse Settings"), function() {
GLib.spawn_command_line_async('gnome-control-center mouse');
});
item.menu.addSettingsAction(_("Mouse Settings"), 'gnome-mouse-panel.desktop');
break;
case GnomeBluetoothApplet.Type.HEADSET:
case GnomeBluetoothApplet.Type.HEADPHONES:
case GnomeBluetoothApplet.Type.OTHER_AUDIO:
item.menu.addAction(_("Sound Settings"), function() {
GLib.spawn_command_line_async('gnome-control-center sound');
});
item.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop');
break;
default:
break;
@ -303,8 +285,6 @@ Indicator.prototype = {
this._showAll(this._fullMenuItems);
if (this._hasDevices)
this._showAll(this._deviceItems);
else
this._deviceSep.actor.hide();
} else {
this._hideAll(this._fullMenuItems);
this._hideAll(this._deviceItems);
@ -351,17 +331,14 @@ Indicator.prototype = {
_cancelRequest: function() {
this._source.destroy();
}
}
});
function Source() {
this._init.apply(this, arguments);
}
Source.prototype = {
__proto__: MessageTray.Source.prototype,
const Source = new Lang.Class({
Name: 'BluetoothSource',
Extends: MessageTray.Source,
_init: function() {
MessageTray.Source.prototype._init.call(this, _("Bluetooth"));
this.parent(_("Bluetooth"));
this._setSummaryIcon(this.createNotificationIcon());
},
@ -375,7 +352,7 @@ Source.prototype = {
}
}));
MessageTray.Source.prototype.notify.call(this, notification);
this.parent(notification);
},
createNotificationIcon: function() {
@ -383,21 +360,17 @@ Source.prototype = {
icon_type: St.IconType.SYMBOLIC,
icon_size: this.ICON_SIZE });
}
}
});
function AuthNotification() {
this._init.apply(this, arguments);
}
AuthNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
const AuthNotification = new Lang.Class({
Name: 'AuthNotification',
Extends: MessageTray.Notification,
_init: function(source, applet, device_path, name, long_name, uuid) {
MessageTray.Notification.prototype._init.call(this,
source,
_("Bluetooth"),
_("Authorization request from %s").format(name),
{ customContent: true });
this.parent(source,
_("Bluetooth"),
_("Authorization request from %s").format(name),
{ customContent: true });
this.setResident(true);
this._applet = applet;
@ -423,21 +396,17 @@ AuthNotification.prototype = {
this.destroy();
}));
}
}
});
function ConfirmNotification() {
this._init.apply(this, arguments);
}
ConfirmNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
const ConfirmNotification = new Lang.Class({
Name: 'ConfirmNotification',
Extends: MessageTray.Notification,
_init: function(source, applet, device_path, name, long_name, pin) {
MessageTray.Notification.prototype._init.call(this,
source,
_("Bluetooth"),
_("Pairing confirmation for %s").format(name),
{ customContent: true });
this.parent(source,
_("Bluetooth"),
_("Pairing confirmation for %s").format(name),
{ customContent: true });
this.setResident(true);
this._applet = applet;
@ -456,21 +425,17 @@ ConfirmNotification.prototype = {
this.destroy();
}));
}
}
});
function PinNotification() {
this._init.apply(this, arguments);
}
PinNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
const PinNotification = new Lang.Class({
Name: 'PinNotification',
Extends: MessageTray.Notification,
_init: function(source, applet, device_path, name, long_name, numeric) {
MessageTray.Notification.prototype._init.call(this,
source,
_("Bluetooth"),
_("Pairing request for %s").format(name),
{ customContent: true });
this.parent(source,
_("Bluetooth"),
_("Pairing request for %s").format(name),
{ customContent: true });
this.setResident(true);
this._applet = applet;
@ -519,7 +484,7 @@ PinNotification.prototype = {
},
grabFocus: function(lockTray) {
MessageTray.Notification.prototype.grabFocus.call(this, lockTray);
this.parent(lockTray);
global.stage.set_key_focus(this._entry);
}
}
});

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const GdkPixbuf = imports.gi.GdkPixbuf;
@ -14,15 +14,12 @@ const PopupMenu = imports.ui.popupMenu;
const PanelMenu = imports.ui.panelMenu;
const Util = imports.misc.util;
function LayoutMenuItem() {
this._init.apply(this, arguments);
}
LayoutMenuItem.prototype = {
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
const LayoutMenuItem = new Lang.Class({
Name: 'LayoutMenuItem',
Extends: PopupMenu.PopupBaseMenuItem,
_init: function(config, id, indicator, long_name) {
PopupMenu.PopupBaseMenuItem.prototype._init.call(this);
this.parent();
this._config = config;
this._id = id;
@ -33,26 +30,25 @@ LayoutMenuItem.prototype = {
},
activate: function(event) {
PopupMenu.PopupBaseMenuItem.prototype.activate.call(this);
this.parent(event);
this._config.lock_group(this._id);
}
};
});
function XKBIndicator() {
this._init.call(this);
}
XKBIndicator.prototype = {
__proto__: PanelMenu.Button.prototype,
const XKBIndicator = new Lang.Class({
Name: 'XKBIndicator',
Extends: PanelMenu.Button,
_init: function() {
PanelMenu.Button.prototype._init.call(this, St.Align.START);
this.parent(0.0);
this._container = new Shell.GenericContainer();
this._container.connect('get-preferred-width', Lang.bind(this, this._get_preferred_width));
this._container.connect('get-preferred-height', Lang.bind(this, this._get_preferred_height));
this._container.connect('allocate', Lang.bind(this, this._allocate));
this.actor.set_child(this._container);
this._container.connect('get-preferred-width', Lang.bind(this, this._containerGetPreferredWidth));
this._container.connect('get-preferred-height', Lang.bind(this, this._containerGetPreferredHeight));
this._container.connect('allocate', Lang.bind(this, this._containerAllocate));
this.actor.add_actor(this._container);
this.actor.add_style_class_name('panel-status-button');
this._iconActor = new St.Icon({ icon_name: 'keyboard', icon_type: St.IconType.SYMBOLIC, style_class: 'system-status-icon' });
this._container.add_actor(this._iconActor);
@ -61,25 +57,45 @@ XKBIndicator.prototype = {
this._showFlags = false;
this._config = Gkbd.Configuration.get();
this._config.connect('changed', Lang.bind(this, this._sync_config));
this._config.connect('group-changed', Lang.bind(this, this._sync_group));
this._config.connect('changed', Lang.bind(this, this._syncConfig));
this._config.connect('group-changed', Lang.bind(this, this._syncGroup));
this._config.start_listen();
this._sync_config();
this._syncConfig();
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addAction(_("Show Keyboard Layout..."), Lang.bind(this, function() {
Main.overview.hide();
Util.spawn(['gkbd-keyboard-display', '-g', String(this._config.get_current_group() + 1)]);
}));
this.menu.addAction(_("Localization Settings"), function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().get_app('gnome-region-panel.desktop');
app.activate(-1);
});
if (global.session_type == Shell.SessionType.USER) {
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, function() {
Main.overview.hide();
Util.spawn(['gkbd-keyboard-display', '-g', String(this._config.get_current_group() + 1)]);
}));
}
this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop');
},
_sync_config: function() {
_adjustGroupNames: function(names) {
// Disambiguate duplicate names with a subscript
// This is O(N^2) to avoid sorting names
// but N <= 4 so who cares?
for (let i = 0; i < names.length; i++) {
let name = names[i];
let cnt = 0;
for (let j = i + 1; j < names.length; j++) {
if (names[j] == name) {
cnt++;
// U+2081 SUBSCRIPT ONE
names[j] = name + String.fromCharCode(0x2081 + cnt);
}
}
if (cnt != 0)
names[i] = name + '\u2081';
}
return names;
},
_syncConfig: function() {
this._showFlags = this._config.if_flags_shown();
if (this._showFlags) {
this._container.set_skip_paint(this._iconActor, false);
@ -101,7 +117,7 @@ XKBIndicator.prototype = {
for (let i = 0; i < this._labelActors.length; i++)
this._labelActors[i].destroy();
let short_names = this._config.get_short_group_names();
let short_names = this._adjustGroupNames(this._config.get_short_group_names());
this._selectedLayout = null;
this._layoutItems = [ ];
@ -126,10 +142,10 @@ XKBIndicator.prototype = {
this._container.set_skip_paint(shortLabel, true);
}
this._sync_group();
this._syncGroup();
},
_sync_group: function() {
_syncGroup: function() {
let selected = this._config.get_current_group();
if (this._selectedLayout) {
@ -152,10 +168,10 @@ XKBIndicator.prototype = {
this._selectedLayout = item;
},
_get_preferred_width: function(container, for_height, alloc) {
/* Here, and in _get_preferred_height, we need to query for the
height of all children, but we ignore the results for those
we don't actually display. */
_containerGetPreferredWidth: function(container, for_height, alloc) {
// Here, and in _containerGetPreferredHeight, we need to query
// for the height of all children, but we ignore the results
// for those we don't actually display.
let max_min_width = 0, max_natural_width = 0;
if (this._showFlags)
[max_min_width, max_natural_width] = this._iconActor.get_preferred_width(for_height);
@ -172,7 +188,7 @@ XKBIndicator.prototype = {
alloc.natural_size = max_natural_width;
},
_get_preferred_height: function(container, for_width, alloc) {
_containerGetPreferredHeight: function(container, for_width, alloc) {
let max_min_height = 0, max_natural_height = 0;
if (this._showFlags)
[max_min_height, max_natural_height] = this._iconActor.get_preferred_height(for_width);
@ -189,7 +205,7 @@ XKBIndicator.prototype = {
alloc.natural_size = max_natural_height;
},
_allocate: function(container, box, flags) {
_containerAllocate: function(container, box, flags) {
// translate box to (0, 0)
box.x2 -= box.x1;
box.x1 = 0;
@ -200,4 +216,4 @@ XKBIndicator.prototype = {
for (let i = 0; i < this._labelActors.length; i++)
this._labelActors[i].allocate_align_fill(box, 0.5, 0, false, false, flags);
}
};
});

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,6 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Gio = imports.gi.Gio;
const DBus = imports.dbus;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
@ -40,31 +39,26 @@ const UPDeviceState = {
PENDING_DISCHARGE: 6
};
const PowerManagerInterface = {
name: 'org.gnome.SettingsDaemon.Power',
methods: [
{ name: 'GetDevices', inSignature: '', outSignature: 'a(susbut)' },
{ name: 'GetPrimaryDevice', inSignature: '', outSignature: '(susbut)' },
],
signals: [
{ name: 'Changed', inSignature: '' },
],
properties: [
{ name: 'Icon', signature: 's', access: 'read' },
]
};
let PowerManagerProxy = DBus.makeProxyClass(PowerManagerInterface);
const PowerManagerInterface = <interface name="org.gnome.SettingsDaemon.Power">
<method name="GetDevices">
<arg type="a(susdut)" direction="out" />
</method>
<method name="GetPrimaryDevice">
<arg type="(susdut)" direction="out" />
</method>
<property name="Icon" type="s" access="read" />
</interface>;
function Indicator() {
this._init.apply(this, arguments);
}
const PowerManagerProxy = Gio.DBusProxy.makeProxyWrapper(PowerManagerInterface);
Indicator.prototype = {
__proto__: PanelMenu.SystemStatusButton.prototype,
const Indicator = new Lang.Class({
Name: 'PowerIndicator',
Extends: PanelMenu.SystemStatusButton,
_init: function() {
PanelMenu.SystemStatusButton.prototype._init.call(this, 'battery-missing');
this._proxy = new PowerManagerProxy(DBus.session, BUS_NAME, OBJECT_PATH);
this.parent('battery-missing', null, _("Battery"));
this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH);
this._deviceItems = [ ];
this._hasPrimary = false;
@ -75,31 +69,26 @@ Indicator.prototype = {
this._batteryItem.addActor(this._primaryPercentage, { align: St.Align.END });
this.menu.addMenuItem(this._batteryItem);
this._deviceSep = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(this._deviceSep);
this._otherDevicePosition = 2;
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._otherDevicePosition = 2;
this.menu.addAction(_("Power Settings"),function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().get_app('gnome-power-panel.desktop');
app.activate(-1);
});
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
this._proxy.connect('Changed', Lang.bind(this, this._devicesChanged));
this._proxy.connect('g-properties-changed',
Lang.bind(this, this._devicesChanged));
this._devicesChanged();
},
_readPrimaryDevice: function() {
this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(device, error) {
this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(result, error) {
if (error) {
this._hasPrimary = false;
this._primaryDeviceId = null;
this._batteryItem.actor.hide();
this._deviceSep.actor.hide();
return;
}
let [device_id, device_type, icon, percentage, state, seconds] = device;
let [[device_id, device_type, icon, percentage, state, seconds]] = result;
if (device_type == UPDeviceType.BATTERY) {
this._hasPrimary = true;
let time = Math.round(seconds / 60);
@ -124,14 +113,11 @@ Indicator.prototype = {
timestring = ngettext("%d minute remaining", "%d minutes remaining", minutes).format(minutes);
this._batteryItem.label.text = timestring;
}
this._primaryPercentage.text = Math.round(percentage) + '%';
this._primaryPercentage.text = C_("percent of battery remaining", "%d%%").format(Math.round(percentage));
this._batteryItem.actor.show();
if (this._deviceItems.length > 0)
this._deviceSep.actor.show();
} else {
this._hasPrimary = false;
this._batteryItem.actor.hide();
this._deviceSep.actor.hide();
}
this._primaryDeviceId = device_id;
@ -139,16 +125,16 @@ Indicator.prototype = {
},
_readOtherDevices: function() {
this._proxy.GetDevicesRemote(Lang.bind(this, function(devices, error) {
this._proxy.GetDevicesRemote(Lang.bind(this, function(result, error) {
this._deviceItems.forEach(function(i) { i.destroy(); });
this._deviceItems = [];
if (error) {
this._deviceSep.actor.hide();
return;
}
let position = 0;
let [devices] = result;
for (let i = 0; i < devices.length; i++) {
let [device_id, device_type] = devices[i];
if (device_type == UPDeviceType.AC_POWER || device_id == this._primaryDeviceId)
@ -159,46 +145,37 @@ Indicator.prototype = {
this.menu.addMenuItem(item, this._otherDevicePosition + position);
position++;
}
if (this._hasPrimary && position > 0)
this._deviceSep.actor.show();
else
this._deviceSep.actor.hide();
}));
},
_devicesChanged: function() {
this._proxy.GetRemote('Icon', Lang.bind(this, function(icon, error) {
if (icon) {
let gicon = Shell.util_icon_from_string (icon);
this.setGIcon(gicon);
this.actor.show();
} else {
this.menu.close();
this.actor.hide();
}
}));
let icon = this._proxy.Icon;
if (icon) {
let gicon = Gio.icon_new_for_string(icon);
this.setGIcon(gicon);
this.actor.show();
} else {
this.menu.close();
this.actor.hide();
}
this._readPrimaryDevice();
this._readOtherDevices();
}
};
});
function DeviceItem() {
this._init.apply(this, arguments);
}
DeviceItem.prototype = {
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
const DeviceItem = new Lang.Class({
Name: 'DeviceItem',
Extends: PopupMenu.PopupBaseMenuItem,
_init: function(device) {
PopupMenu.PopupBaseMenuItem.prototype._init.call(this, { reactive: false });
this.parent({ reactive: false });
let [device_id, device_type, icon, percentage, state, time] = device;
this._box = new St.BoxLayout({ style_class: 'popup-device-menu-item' });
this._label = new St.Label({ text: this._deviceTypeToString(device_type) });
this._icon = new St.Icon({ gicon: Shell.util_icon_from_string(icon),
this._icon = new St.Icon({ gicon: Gio.icon_new_for_string(icon),
icon_type: St.IconType.SYMBOLIC,
style_class: 'popup-menu-icon' });
@ -206,7 +183,7 @@ DeviceItem.prototype = {
this._box.add_actor(this._label);
this.addActor(this._box);
let percentLabel = new St.Label({ text: '%d%%'.format(Math.round(percentage)) });
let percentLabel = new St.Label({ text: C_("percent of battery remaining", "%d%%").format(Math.round(percentage)) });
this.addActor(percentLabel, { align: St.Align.END });
},
@ -238,4 +215,4 @@ DeviceItem.prototype = {
return _("Unknown");
}
}
}
});

View File

@ -1,7 +1,6 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const DBus = imports.dbus;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Shell = imports.gi.Shell;
@ -18,24 +17,20 @@ const VOLUME_ADJUSTMENT_STEP = 0.05; /* Volume adjustment step in % */
const VOLUME_NOTIFY_ID = 1;
function Indicator() {
this._init.apply(this, arguments);
}
Indicator.prototype = {
__proto__: PanelMenu.SystemStatusButton.prototype,
const Indicator = new Lang.Class({
Name: 'VolumeIndicator',
Extends: PanelMenu.SystemStatusButton,
_init: function() {
PanelMenu.SystemStatusButton.prototype._init.call(this, 'audio-volume-muted', null);
this.parent('audio-volume-muted', null, _("Volume"));
this._control = new Gvc.MixerControl({ name: 'GNOME Shell Volume Control' });
this._control.connect('ready', Lang.bind(this, this._onControlReady));
this._control.connect('state-changed', Lang.bind(this, this._onControlStateChanged));
this._control.connect('default-sink-changed', Lang.bind(this, this._readOutput));
this._control.connect('default-source-changed', Lang.bind(this, this._readInput));
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
this._volumeMax = this._control.get_vol_max_norm();
this._volumeMaxAmplified = this._control.get_vol_max_amplified();
this._output = null;
this._outputVolumeId = 0;
@ -47,8 +42,7 @@ Indicator.prototype = {
this.menu.addMenuItem(this._outputTitle);
this.menu.addMenuItem(this._outputSlider);
this._separator = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(this._separator);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._input = null;
this._inputVolumeId = 0;
@ -61,31 +55,19 @@ Indicator.prototype = {
this.menu.addMenuItem(this._inputSlider);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addAction(_("Sound Settings"), function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().get_app('gnome-sound-panel.desktop');
app.activate(-1);
});
this.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop');
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
this._control.open();
},
_getMaxVolume: function(property) {
if (this[property].get_can_decibel())
return this._volumeMaxAmplified;
else
return this._volumeMax;
},
_onScrollEvent: function(actor, event) {
let direction = event.get_scroll_direction();
let currentVolume = this._output.volume;
let maxVolume = this._getMaxVolume('_output');
if (direction == Clutter.ScrollDirection.DOWN) {
let prev_muted = this._output.is_muted;
this._output.volume = Math.max(0, currentVolume - maxVolume * VOLUME_ADJUSTMENT_STEP);
this._output.volume = Math.max(0, currentVolume - this._volumeMax * VOLUME_ADJUSTMENT_STEP);
if (this._output.volume < 1) {
this._output.volume = 0;
if (!prev_muted)
@ -94,7 +76,7 @@ Indicator.prototype = {
this._output.push_volume();
}
else if (direction == Clutter.ScrollDirection.UP) {
this._output.volume = Math.min(maxVolume, currentVolume + maxVolume * VOLUME_ADJUSTMENT_STEP);
this._output.volume = Math.min(this._volumeMax, currentVolume + this._volumeMax * VOLUME_ADJUSTMENT_STEP);
this._output.change_is_muted(false);
this._output.push_volume();
}
@ -102,9 +84,14 @@ Indicator.prototype = {
this._notifyVolumeChange();
},
_onControlReady: function() {
this._readOutput();
this._readInput();
_onControlStateChanged: function() {
if (this._control.get_state() == Gvc.MixerControlState.READY) {
this._readOutput();
this._readInput();
this.actor.show();
} else {
this.actor.hide();
}
},
_readOutput: function() {
@ -140,7 +127,6 @@ Indicator.prototype = {
this._mutedChanged (null, null, '_input');
this._volumeChanged (null, null, '_input');
} else {
this._separator.actor.hide();
this._inputTitle.actor.hide();
this._inputSlider.actor.hide();
}
@ -163,22 +149,19 @@ Indicator.prototype = {
}
}
if (showInput) {
this._separator.actor.show();
this._inputTitle.actor.show();
this._inputSlider.actor.show();
} else {
this._separator.actor.hide();
this._inputTitle.actor.hide();
this._inputSlider.actor.hide();
}
},
_volumeToIcon: function(volume) {
let maxVolume = this._getMaxVolume('_output');
if (volume <= 0) {
return 'audio-volume-muted';
} else {
let n = Math.floor(3 * volume / maxVolume) + 1;
let n = Math.floor(3 * volume / this._volumeMax) + 1;
if (n < 2)
return 'audio-volume-low';
if (n >= 3)
@ -192,7 +175,7 @@ Indicator.prototype = {
log ('Volume slider changed for %s, but %s does not exist'.format(property, property));
return;
}
let volume = value * this._getMaxVolume(property);
let volume = value * this._volumeMax;
let prev_muted = this[property].is_muted;
if (volume < 1) {
this[property].volume = 0;
@ -214,8 +197,7 @@ Indicator.prototype = {
_mutedChanged: function(object, param_spec, property) {
let muted = this[property].is_muted;
let slider = this[property+'Slider'];
let maxVolume = this._getMaxVolume(property);
slider.setValue(muted ? 0 : (this[property].volume / maxVolume));
slider.setValue(muted ? 0 : (this[property].volume / this._volumeMax));
if (property == '_output') {
if (muted)
this.setIcon('audio-volume-muted');
@ -225,9 +207,8 @@ Indicator.prototype = {
},
_volumeChanged: function(object, param_spec, property) {
let maxVolume = this._getMaxVolume(property);
this[property+'Slider'].setValue(this[property].volume / maxVolume);
this[property+'Slider'].setValue(this[property].volume / this._volumeMax);
if (property == '_output' && !this._output.is_muted)
this.setIcon(this._volumeToIcon(this._output.volume));
}
};
});

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Lang = imports.lang;
const Shell = imports.gi.Shell;
@ -23,11 +23,9 @@ const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
'ibus-ui-gtk': 'input-method'
};
function StatusIconDispatcher() {
this._init();
}
const StatusIconDispatcher = new Lang.Class({
Name: 'StatusIconDispatcher',
StatusIconDispatcher.prototype = {
_init: function() {
this._traymanager = new Shell.TrayManager();
this._traymanager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded));
@ -61,5 +59,5 @@ StatusIconDispatcher.prototype = {
else
this.emit('message-icon-removed', icon);
}
};
});
Signals.addSignalMethods(StatusIconDispatcher.prototype);

View File

@ -1,345 +0,0 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Gdm = imports.gi.Gdm;
const DBus = imports.dbus;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Tp = imports.gi.TelepathyGLib;
const UPowerGlib = imports.gi.UPowerGlib;
const GnomeSession = imports.misc.gnomeSession;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const ScreenSaver = imports.misc.screenSaver;
const Util = imports.misc.util;
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
const DISABLE_LOG_OUT_KEY = 'disable-log-out';
// Adapted from gdm/gui/user-switch-applet/applet.c
//
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
// Copyright (C) 2008,2009 Red Hat, Inc.
function StatusMenuButton() {
this._init();
}
StatusMenuButton.prototype = {
__proto__: PanelMenu.Button.prototype,
_init: function() {
PanelMenu.Button.prototype._init.call(this, 0.0);
let box = new St.BoxLayout({ name: 'panelStatusMenu' });
this.actor.set_child(box);
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
this._gdm = Gdm.UserManager.ref_default();
this._gdm.queue_load();
this._user = this._gdm.get_user(GLib.get_user_name());
this._presence = new GnomeSession.Presence();
this._presenceItems = {};
this._session = new GnomeSession.SessionManager();
this._haveShutdown = true;
this._account_mgr = Tp.AccountManager.dup()
this._upClient = new UPowerGlib.Client();
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._iconBox = new St.Bin();
box.add(this._iconBox, { y_align: St.Align.MIDDLE, y_fill: false });
let textureCache = St.TextureCache.get_default();
this._availableIcon = new St.Icon({ icon_name: 'user-available', style_class: 'popup-menu-icon' });
this._busyIcon = new St.Icon({ icon_name: 'user-busy', style_class: 'popup-menu-icon' });
this._invisibleIcon = new St.Icon({ icon_name: 'user-invisible', style_class: 'popup-menu-icon' });
this._idleIcon = new St.Icon({ icon_name: 'user-idle', style_class: 'popup-menu-icon' });
this._presence.connect('StatusChanged', Lang.bind(this, this._updatePresenceIcon));
this._presence.getStatus(Lang.bind(this, this._updatePresenceIcon));
this._name = new St.Label();
box.add(this._name, { y_align: St.Align.MIDDLE, y_fill: false });
this._userLoadedId = this._user.connect('notify::is-loaded', Lang.bind(this, this._updateUserName));
this._userChangedId = this._user.connect('changed', Lang.bind(this, this._updateUserName));
this._createSubMenu();
this._gdm.connect('notify::is-loaded', Lang.bind(this, this._updateSwitchUser));
this._gdm.connect('user-added', Lang.bind(this, this._updateSwitchUser));
this._gdm.connect('user-removed', Lang.bind(this, this._updateSwitchUser));
this._lockdownSettings.connect('changed::' + DISABLE_USER_SWITCH_KEY,
Lang.bind(this, this._updateSwitchUser));
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
Lang.bind(this, this._updateLogout));
this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
Lang.bind(this, this._updateLockScreen));
this._updateSwitchUser();
this._updateLogout();
this._updateLockScreen();
// Whether shutdown is available or not depends on both lockdown
// settings (disable-log-out) and Polkit policy - the latter doesn't
// notify, so we update the menu item each time the menu opens or
// the lockdown setting changes, which should be close enough.
this.menu.connect('open-state-changed', Lang.bind(this,
function(menu, open) {
if (open)
this._updateHaveShutdown();
}));
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
Lang.bind(this, this._updateHaveShutdown));
this._upClient.connect('notify::can-suspend', Lang.bind(this, this._updateSuspendOrPowerOff));
},
_onDestroy: function() {
this._user.disconnect(this._userLoadedId);
this._user.disconnect(this._userChangedId);
},
_updateUserName: function() {
if (this._user.is_loaded)
this._name.set_text(this._user.get_real_name());
else
this._name.set_text("");
},
_updateSessionSeparator: function() {
let sessionItemsVisible = this._loginScreenItem.actor.visible ||
this._logoutItem.actor.visible ||
this._lockScreenItem.actor.visible;
let showSessionSeparator = sessionItemsVisible &&
this._suspendOrPowerOffItem.actor.visible;
let showSettingsSeparator = sessionItemsVisible ||
this._suspendOrPowerOffItem.actor.visible;
if (showSessionSeparator)
this._sessionSeparator.actor.show();
else
this._sessionSeparator.actor.hide();
if (showSettingsSeparator)
this._settingsSeparator.actor.show();
else
this._settingsSeparator.actor.hide();
},
_updateSwitchUser: function() {
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
if (allowSwitch && this._gdm.can_switch ())
this._loginScreenItem.actor.show();
else
this._loginScreenItem.actor.hide();
this._updateSessionSeparator();
},
_updateLogout: function() {
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
if (allowLogout)
this._logoutItem.actor.show();
else
this._logoutItem.actor.hide();
this._updateSessionSeparator();
},
_updateLockScreen: function() {
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
if (allowLockScreen)
this._lockScreenItem.actor.show();
else
this._lockScreenItem.actor.hide();
this._updateSessionSeparator();
},
_updateHaveShutdown: function() {
this._session.CanShutdownRemote(Lang.bind(this,
function(result, error) {
if (!error) {
this._haveShutdown = result;
this._updateSuspendOrPowerOff();
}
}));
},
_updateSuspendOrPowerOff: function() {
this._haveSuspend = this._upClient.get_can_suspend();
if (!this._suspendOrPowerOffItem)
return;
if (!this._haveShutdown && !this._haveSuspend)
this._suspendOrPowerOffItem.actor.hide();
else
this._suspendOrPowerOffItem.actor.show();
this._updateSessionSeparator();
// If we can't suspend show Power Off... instead
// and disable the alt key
if (!this._haveSuspend) {
this._suspendOrPowerOffItem.updateText(_("Power Off..."), null);
} else if (!this._haveShutdown) {
this._suspendOrPowerOffItem.updateText(_("Suspend"), null);
} else {
this._suspendOrPowerOffItem.updateText(_("Suspend"), _("Power Off..."));
}
},
_updatePresenceIcon: function(presence, status) {
if (status == GnomeSession.PresenceStatus.AVAILABLE)
this._iconBox.child = this._availableIcon;
else if (status == GnomeSession.PresenceStatus.BUSY)
this._iconBox.child = this._busyIcon;
else if (status == GnomeSession.PresenceStatus.INVISIBLE)
this._iconBox.child = this._invisibleIcon;
else
this._iconBox.child = this._idleIcon;
for (let itemStatus in this._presenceItems)
this._presenceItems[itemStatus].setShowDot(itemStatus == status);
},
_createSubMenu: function() {
let item;
item = new PopupMenu.PopupImageMenuItem(_("Available"), 'user-available');
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSession.PresenceStatus.AVAILABLE));
this.menu.addMenuItem(item);
this._presenceItems[GnomeSession.PresenceStatus.AVAILABLE] = item;
item = new PopupMenu.PopupImageMenuItem(_("Busy"), 'user-busy');
item.connect('activate', Lang.bind(this, this._setPresenceStatus, GnomeSession.PresenceStatus.BUSY));
this.menu.addMenuItem(item);
this._presenceItems[GnomeSession.PresenceStatus.BUSY] = item;
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("My Account"));
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("System Settings"));
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
this.menu.addMenuItem(item);
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
this._settingsSeparator = item;
item = new PopupMenu.PopupMenuItem(_("Lock Screen"));
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
this.menu.addMenuItem(item);
this._lockScreenItem = item;
item = new PopupMenu.PopupMenuItem(_("Switch User"));
item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
this.menu.addMenuItem(item);
this._loginScreenItem = item;
item = new PopupMenu.PopupMenuItem(_("Log Out..."));
item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
this.menu.addMenuItem(item);
this._logoutItem = item;
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
this._sessionSeparator = item;
item = new PopupMenu.PopupAlternatingMenuItem(_("Suspend"),
_("Power Off..."));
this.menu.addMenuItem(item);
this._suspendOrPowerOffItem = item;
item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
this._updateSuspendOrPowerOff();
},
_setPresenceStatus: function(item, event, status) {
this._presence.setStatus(status);
this._setIMStatus(status);
},
_onMyAccountActivate: function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().get_app('gnome-user-accounts-panel.desktop');
app.activate(-1);
},
_onPreferencesActivate: function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().get_app('gnome-control-center.desktop');
app.activate(-1);
},
_onLockScreenActivate: function() {
Main.overview.hide();
this._screenSaverProxy.LockRemote();
},
_onLoginScreenActivate: function() {
Main.overview.hide();
// Ensure we only move to GDM after the screensaver has activated; in some
// OS configurations, the X server may block event processing on VT switch
this._screenSaverProxy.SetActiveRemote(true, Lang.bind(this, function() {
this._gdm.goto_login_session();
}));
},
_onQuitSessionActivate: function() {
Main.overview.hide();
this._session.LogoutRemote(0);
},
_onSuspendOrPowerOffActivate: function() {
Main.overview.hide();
if (this._haveSuspend &&
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
// Ensure we only suspend after the screensaver has activated
this._screenSaverProxy.SetActiveRemote(true, Lang.bind(this, function() {
this._upClient.suspend_sync(null);
}));
} else {
this._session.ShutdownRemote();
}
},
_setIMStatus: function(session_status) {
let [presence_type, presence_status, msg] = this._account_mgr.get_most_available_presence();
let type, status;
// We change the IM presence only if there are connected accounts
if (presence_type == Tp.ConnectionPresenceType.UNSET ||
presence_type == Tp.ConnectionPresenceType.OFFLINE ||
presence_type == Tp.ConnectionPresenceType.UNKNOWN ||
presence_type == Tp.ConnectionPresenceType.ERROR)
return;
if (session_status == GnomeSession.PresenceStatus.AVAILABLE) {
type = Tp.ConnectionPresenceType.AVAILABLE;
status = "available";
}
else if (session_status == GnomeSession.PresenceStatus.BUSY) {
type = Tp.ConnectionPresenceType.BUSY;
status = "busy";
}
else {
return;
}
this._account_mgr.set_all_requested_presences(type, status, msg);
}
};

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;
@ -202,11 +202,9 @@ function registerSpecialPropertySplitter(name, splitFunction, parameters) {
// time updates; even better is to pay attention to the vertical
// vblank and sync to that when possible.)
//
function ClutterFrameTicker() {
this._init();
}
const ClutterFrameTicker = new Lang.Class({
Name: 'ClutterFrameTicker',
ClutterFrameTicker.prototype = {
FRAME_RATE : 60,
_init : function() {
@ -261,6 +259,6 @@ ClutterFrameTicker.prototype = {
this._startTime = -1;
global.end_work();
}
};
});
Signals.addSignalMethods(ClutterFrameTicker.prototype);

729
js/ui/userMenu.js Normal file
View File

@ -0,0 +1,729 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
const AccountsService = imports.gi.AccountsService;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Pango = imports.gi.Pango;
const Shell = imports.gi.Shell;
const St = imports.gi.St;
const Tp = imports.gi.TelepathyGLib;
const UPowerGlib = imports.gi.UPowerGlib;
const GnomeSession = imports.misc.gnomeSession;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const ScreenSaver = imports.misc.screenSaver;
const Util = imports.misc.util;
const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown';
const DISABLE_USER_SWITCH_KEY = 'disable-user-switching';
const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen';
const DISABLE_LOG_OUT_KEY = 'disable-log-out';
const DIALOG_ICON_SIZE = 64;
const IMStatus = {
AVAILABLE: 0,
BUSY: 1,
HIDDEN: 2,
AWAY: 3,
IDLE: 4,
OFFLINE: 5,
LAST: 6
};
// Adapted from gdm/gui/user-switch-applet/applet.c
//
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
// Copyright (C) 2008,2009 Red Hat, Inc.
const IMStatusItem = new Lang.Class({
Name: 'IMStatusItem',
Extends: PopupMenu.PopupBaseMenuItem,
_init: function(label, iconName) {
this.parent();
this.actor.add_style_class_name('status-chooser-status-item');
this._icon = new St.Icon({ style_class: 'popup-menu-icon' });
this.addActor(this._icon);
if (iconName)
this._icon.icon_name = iconName;
this.label = new St.Label({ text: label });
this.addActor(this.label);
}
});
const IMUserNameItem = new Lang.Class({
Name: 'IMUserNameItem',
Extends: PopupMenu.PopupBaseMenuItem,
_init: function() {
this.parent({ reactive: false,
style_class: 'status-chooser-user-name' });
this._wrapper = new Shell.GenericContainer();
this._wrapper.connect('get-preferred-width',
Lang.bind(this, this._wrapperGetPreferredWidth));
this._wrapper.connect('get-preferred-height',
Lang.bind(this, this._wrapperGetPreferredHeight));
this._wrapper.connect('allocate',
Lang.bind(this, this._wrapperAllocate));
this.addActor(this._wrapper, { expand: true, span: -1 });
this.label = new St.Label();
this.label.clutter_text.set_line_wrap(true);
this.label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
this._wrapper.add_actor(this.label);
},
_wrapperGetPreferredWidth: function(actor, forHeight, alloc) {
alloc.min_size = 1;
alloc.natural_size = 1;
},
_wrapperGetPreferredHeight: function(actor, forWidth, alloc) {
[alloc.min_size, alloc.natural_size] = this.label.get_preferred_height(forWidth);
},
_wrapperAllocate: function(actor, box, flags) {
this.label.allocate(box, flags);
}
});
const IMStatusChooserItem = new Lang.Class({
Name: 'IMStatusChooserItem',
Extends: PopupMenu.PopupBaseMenuItem,
_init: function() {
this.parent({ reactive: false,
style_class: 'status-chooser' });
this._iconBin = new St.Button({ style_class: 'status-chooser-user-icon' });
this.addActor(this._iconBin);
this._iconBin.connect('clicked', Lang.bind(this,
function() {
this.activate();
}));
this._section = new PopupMenu.PopupMenuSection();
this.addActor(this._section.actor);
this._name = new IMUserNameItem();
this._section.addMenuItem(this._name);
this._combo = new PopupMenu.PopupComboBoxMenuItem({ style_class: 'status-chooser-combo' });
this._section.addMenuItem(this._combo);
let item;
item = new IMStatusItem(_("Available"), 'user-available');
this._combo.addMenuItem(item, IMStatus.AVAILABLE);
item = new IMStatusItem(_("Busy"), 'user-busy');
this._combo.addMenuItem(item, IMStatus.BUSY);
item = new IMStatusItem(_("Hidden"), 'user-invisible');
this._combo.addMenuItem(item, IMStatus.HIDDEN);
item = new IMStatusItem(_("Away"), 'user-away');
this._combo.addMenuItem(item, IMStatus.AWAY);
item = new IMStatusItem(_("Idle"), 'user-idle');
this._combo.addMenuItem(item, IMStatus.IDLE);
item = new IMStatusItem(_("Unavailable"), 'user-offline');
this._combo.addMenuItem(item, IMStatus.OFFLINE);
this._combo.connect('active-item-changed',
Lang.bind(this, this._changeIMStatus));
this._presence = new GnomeSession.Presence();
this._presence.connectSignal('StatusChanged', Lang.bind(this, function(proxy, senderName, [status]) {
this._sessionStatusChanged(status);
}));
this._sessionPresenceRestored = false;
this._imPresenceRestored = false;
this._currentPresence = undefined;
this._accountMgr = Tp.AccountManager.dup();
this._accountMgr.connect('most-available-presence-changed',
Lang.bind(this, this._IMStatusChanged));
this._accountMgr.connect('account-enabled',
Lang.bind(this, this._IMAccountsChanged));
this._accountMgr.connect('account-disabled',
Lang.bind(this, this._IMAccountsChanged));
this._accountMgr.connect('account-removed',
Lang.bind(this, this._IMAccountsChanged));
this._accountMgr.prepare_async(null, Lang.bind(this,
function(mgr) {
let [presence, status, msg] = mgr.get_most_available_presence();
let savedPresence = global.settings.get_int('saved-im-presence');
this._IMAccountsChanged(mgr);
if (savedPresence == presence) {
this._IMStatusChanged(mgr, presence, status, msg);
} else {
this._setComboboxPresence(savedPresence);
status = this._statusForPresence(savedPresence);
msg = msg ? msg : '';
mgr.set_all_requested_presences(savedPresence, status, msg);
}
}));
this._userManager = AccountsService.UserManager.get_default();
this._user = this._userManager.get_user(GLib.get_user_name());
this._userLoadedId = this._user.connect('notify::is-loaded',
Lang.bind(this,
this._updateUser));
this._userChangedId = this._user.connect('changed',
Lang.bind(this,
this._updateUser));
this.actor.connect('notify::mapped', Lang.bind(this, function() {
if (this.actor.mapped)
this._updateUser();
}));
},
destroy: function() {
// clean up signal handlers
if (this._userLoadedId != 0) {
this._user.disconnect(this._userLoadedId);
this._userLoadedId = 0;
}
if (this._userChangedId != 0) {
this._user.disconnect(this._userChangedId);
this._userChangedId = 0;
}
this.parent();
},
// Override getColumnWidths()/setColumnWidths() to make the item
// independent from the overall column layout of the menu
getColumnWidths: function() {
return [];
},
setColumnWidths: function(widths) {
},
_updateUser: function() {
let iconFile = null;
if (this._user.is_loaded) {
this._name.label.set_text(this._user.get_real_name());
iconFile = this._user.get_icon_file();
if (!GLib.file_test(iconFile, GLib.FileTest.EXISTS))
iconFile = null;
} else {
this._name.label.set_text("");
}
if (iconFile)
this._setIconFromFile(iconFile);
else
this._setIconFromName('avatar-default');
},
_setIconFromFile: function(iconFile) {
this._iconBin.set_style('background-image: url("' + iconFile + '");' +
'background-size: contain;');
this._iconBin.child = null;
},
_setIconFromName: function(iconName) {
this._iconBin.set_style(null);
if (iconName != null) {
let textureCache = St.TextureCache.get_default();
let icon = textureCache.load_icon_name(this._iconBin.get_theme_node(),
iconName,
St.IconType.SYMBOLIC,
DIALOG_ICON_SIZE);
this._iconBin.child = icon;
this._iconBin.show();
} else {
this._iconBin.child = null;
this._iconBin.hide();
}
},
_statusForPresence: function(presence) {
switch(presence) {
case Tp.ConnectionPresenceType.AVAILABLE:
return 'available';
case Tp.ConnectionPresenceType.BUSY:
return 'busy';
case Tp.ConnectionPresenceType.OFFLINE:
return 'offline';
case Tp.ConnectionPresenceType.HIDDEN:
return 'hidden';
case Tp.ConnectionPresenceType.AWAY:
return 'away';
case Tp.ConnectionPresenceType.EXTENDED_AWAY:
return 'xa';
default:
return 'unknown';
}
},
_IMAccountsChanged: function(mgr) {
let accounts = mgr.get_valid_accounts().filter(function(account) {
return account.enabled;
});
this._combo.setSensitive(accounts.length > 0);
},
_IMStatusChanged: function(accountMgr, presence, status, message) {
if (!this._imPresenceRestored)
this._imPresenceRestored = true;
if (presence == this._currentPresence)
return;
this._currentPresence = presence;
this._setComboboxPresence(presence);
if (!this._sessionPresenceRestored) {
this._sessionStatusChanged(this._presence.status);
return;
}
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
this._presence.status = GnomeSession.PresenceStatus.AVAILABLE;
// We ignore the actual value of _expectedPresence and never safe
// the first presence change after an "automatic" change, assuming
// that it is the response to our request; this is to account for
// mission control falling back to "similar" presences if an account
// type does not implement the requested presence.
if (!this._expectedPresence)
global.settings.set_int('saved-im-presence', presence);
else
this._expectedPresence = undefined;
},
_setComboboxPresence: function(presence) {
let activatedItem;
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
activatedItem = IMStatus.AVAILABLE;
else if (presence == Tp.ConnectionPresenceType.BUSY)
activatedItem = IMStatus.BUSY;
else if (presence == Tp.ConnectionPresenceType.HIDDEN)
activatedItem = IMStatus.HIDDEN;
else if (presence == Tp.ConnectionPresenceType.AWAY)
activatedItem = IMStatus.AWAY;
else if (presence == Tp.ConnectionPresenceType.EXTENDED_AWAY)
activatedItem = IMStatus.IDLE;
else
activatedItem = IMStatus.OFFLINE;
this._combo.setActiveItem(activatedItem);
for (let i = 0; i < IMStatus.LAST; i++) {
if (i == IMStatus.AVAILABLE || i == IMStatus.OFFLINE)
continue; // always visible
this._combo.setItemVisible(i, i == activatedItem);
}
},
_changeIMStatus: function(menuItem, id) {
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
let newPresence, status;
if (id == IMStatus.AVAILABLE) {
newPresence = Tp.ConnectionPresenceType.AVAILABLE;
} else if (id == IMStatus.OFFLINE) {
newPresence = Tp.ConnectionPresenceType.OFFLINE;
} else
return;
status = this._statusForPresence(newPresence);
msg = msg ? msg : '';
this._accountMgr.set_all_requested_presences(newPresence, status, msg);
},
getIMPresenceForSessionStatus: function(sessionStatus) {
// Restore the last user-set presence when coming back from
// BUSY/IDLE (otherwise the last user-set presence matches
// the current one)
if (sessionStatus == GnomeSession.PresenceStatus.AVAILABLE)
return global.settings.get_int('saved-im-presence');
if (sessionStatus == GnomeSession.PresenceStatus.BUSY) {
// Only change presence if the current one is "more present" than
// busy, or if coming back from idle
if (this._currentPresence == Tp.ConnectionPresenceType.AVAILABLE ||
this._currentPresence == Tp.ConnectionPresenceType.EXTENDED_AWAY)
return Tp.ConnectionPresenceType.BUSY;
}
if (sessionStatus == GnomeSession.PresenceStatus.IDLE) {
// Only change presence if the current one is "more present" than
// idle
if (this._currentPresence != Tp.ConnectionPresenceType.OFFLINE &&
this._currentPresence != Tp.ConnectionPresenceType.HIDDEN)
return Tp.ConnectionPresenceType.EXTENDED_AWAY;
}
return this._currentPresence;
},
_sessionStatusChanged: function(sessionStatus) {
if (!this._imPresenceRestored)
return;
if (!this._sessionPresenceRestored) {
let savedStatus = global.settings.get_int('saved-session-presence');
if (sessionStatus != savedStatus) {
this._presence.status = savedStatus;
return;
}
this._sessionPresenceRestored = true;
}
global.settings.set_int('saved-session-presence', sessionStatus);
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
let newPresence, status;
let newPresence = this.getIMPresenceForSessionStatus(sessionStatus);
if (!newPresence || newPresence == presence)
return;
status = this._statusForPresence(newPresence);
msg = msg ? msg : '';
this._expectedPresence = newPresence;
this._accountMgr.set_all_requested_presences(newPresence, status, msg);
}
});
const UserMenuButton = new Lang.Class({
Name: 'UserMenuButton',
Extends: PanelMenu.Button,
_init: function() {
this.parent(0.0);
let box = new St.BoxLayout({ name: 'panelUserMenu' });
this.actor.add_actor(box);
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });
this._userManager = AccountsService.UserManager.get_default();
this._user = this._userManager.get_user(GLib.get_user_name());
this._presence = new GnomeSession.Presence();
this._session = new GnomeSession.SessionManager();
this._haveShutdown = true;
this._accountMgr = Tp.AccountManager.dup();
this._upClient = new UPowerGlib.Client();
this._screenSaverProxy = new ScreenSaver.ScreenSaverProxy();
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._iconBox = new St.Bin();
box.add(this._iconBox, { y_align: St.Align.MIDDLE, y_fill: false });
let textureCache = St.TextureCache.get_default();
this._offlineIcon = new St.Icon({ icon_name: 'user-offline',
style_class: 'popup-menu-icon' });
this._availableIcon = new St.Icon({ icon_name: 'user-available',
style_class: 'popup-menu-icon' });
this._busyIcon = new St.Icon({ icon_name: 'user-busy',
style_class: 'popup-menu-icon' });
this._invisibleIcon = new St.Icon({ icon_name: 'user-invisible',
style_class: 'popup-menu-icon' });
this._awayIcon = new St.Icon({ icon_name: 'user-away',
style_class: 'popup-menu-icon' });
this._idleIcon = new St.Icon({ icon_name: 'user-idle',
style_class: 'popup-menu-icon' });
this._accountMgr.connect('most-available-presence-changed',
Lang.bind(this, this._updatePresenceIcon));
this._accountMgr.prepare_async(null, Lang.bind(this,
function(mgr) {
let [presence, s, msg] = mgr.get_most_available_presence();
this._updatePresenceIcon(mgr, presence, s, msg);
}));
this._name = new St.Label();
this.actor.label_actor = this._name;
box.add(this._name, { y_align: St.Align.MIDDLE, y_fill: false });
this._userLoadedId = this._user.connect('notify::is-loaded', Lang.bind(this, this._updateUserName));
this._userChangedId = this._user.connect('changed', Lang.bind(this, this._updateUserName));
this._updateUserName();
this._createSubMenu();
this._updateSwitch(this._presence.status);
this._presence.connectSignal('StatusChanged', Lang.bind(this, function (proxy, senderName, [status]) {
this._updateSwitch(status);
}));
this._userManager.connect('notify::is-loaded',
Lang.bind(this, this._updateSwitchUser));
this._userManager.connect('notify::has-multiple-users',
Lang.bind(this, this._updateSwitchUser));
this._userManager.connect('user-added',
Lang.bind(this, this._updateSwitchUser));
this._userManager.connect('user-removed',
Lang.bind(this, this._updateSwitchUser));
this._lockdownSettings.connect('changed::' + DISABLE_USER_SWITCH_KEY,
Lang.bind(this, this._updateSwitchUser));
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
Lang.bind(this, this._updateLogout));
this._lockdownSettings.connect('changed::' + DISABLE_LOCK_SCREEN_KEY,
Lang.bind(this, this._updateLockScreen));
this._updateSwitchUser();
this._updateLogout();
this._updateLockScreen();
// Whether shutdown is available or not depends on both lockdown
// settings (disable-log-out) and Polkit policy - the latter doesn't
// notify, so we update the menu item each time the menu opens or
// the lockdown setting changes, which should be close enough.
this.menu.connect('open-state-changed', Lang.bind(this,
function(menu, open) {
if (open)
this._updateHaveShutdown();
}));
this._lockdownSettings.connect('changed::' + DISABLE_LOG_OUT_KEY,
Lang.bind(this, this._updateHaveShutdown));
this._upClient.connect('notify::can-suspend', Lang.bind(this, this._updateSuspendOrPowerOff));
},
_onDestroy: function() {
this._user.disconnect(this._userLoadedId);
this._user.disconnect(this._userChangedId);
},
_updateUserName: function() {
if (this._user.is_loaded)
this._name.set_text(this._user.get_real_name());
else
this._name.set_text("");
},
_updateSwitchUser: function() {
let allowSwitch = !this._lockdownSettings.get_boolean(DISABLE_USER_SWITCH_KEY);
if (allowSwitch &&
this._userManager.can_switch() &&
this._userManager.has_multiple_users)
this._loginScreenItem.actor.show();
else
this._loginScreenItem.actor.hide();
},
_updateLogout: function() {
let allowLogout = !this._lockdownSettings.get_boolean(DISABLE_LOG_OUT_KEY);
if (allowLogout)
this._logoutItem.actor.show();
else
this._logoutItem.actor.hide();
},
_updateLockScreen: function() {
let allowLockScreen = !this._lockdownSettings.get_boolean(DISABLE_LOCK_SCREEN_KEY);
if (allowLockScreen)
this._lockScreenItem.actor.show();
else
this._lockScreenItem.actor.hide();
},
_updateHaveShutdown: function() {
this._session.CanShutdownRemote(Lang.bind(this,
function(result, error) {
if (!error) {
this._haveShutdown = result;
this._updateSuspendOrPowerOff();
}
}));
},
_updateSuspendOrPowerOff: function() {
this._haveSuspend = this._upClient.get_can_suspend();
if (!this._suspendOrPowerOffItem)
return;
if (!this._haveShutdown && !this._haveSuspend)
this._suspendOrPowerOffItem.actor.hide();
else
this._suspendOrPowerOffItem.actor.show();
// If we can't suspend show Power Off... instead
// and disable the alt key
if (!this._haveSuspend) {
this._suspendOrPowerOffItem.updateText(_("Power Off..."), null);
} else if (!this._haveShutdown) {
this._suspendOrPowerOffItem.updateText(_("Suspend"), null);
} else {
this._suspendOrPowerOffItem.updateText(_("Suspend"), _("Power Off..."));
}
},
_updateSwitch: function(status) {
let active = status == GnomeSession.PresenceStatus.AVAILABLE;
this._notificationsSwitch.setToggleState(active);
},
_updatePresenceIcon: function(accountMgr, presence, status, message) {
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
this._iconBox.child = this._availableIcon;
else if (presence == Tp.ConnectionPresenceType.BUSY)
this._iconBox.child = this._busyIcon;
else if (presence == Tp.ConnectionPresenceType.HIDDEN)
this._iconBox.child = this._invisibleIcon;
else if (presence == Tp.ConnectionPresenceType.AWAY)
this._iconBox.child = this._awayIcon;
else if (presence == Tp.ConnectionPresenceType.EXTENDED_AWAY)
this._iconBox.child = this._idleIcon;
else
this._iconBox.child = this._offlineIcon;
},
_createSubMenu: function() {
let item;
item = new IMStatusChooserItem();
item.connect('activate', Lang.bind(this, this._onMyAccountActivate));
this.menu.addMenuItem(item);
this._statusChooser = item;
item = new PopupMenu.PopupSwitchMenuItem(_("Notifications"));
item.connect('toggled', Lang.bind(this, this._updatePresenceStatus));
this.menu.addMenuItem(item);
this._notificationsSwitch = item;
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("Online Accounts"));
item.connect('activate', Lang.bind(this, this._onOnlineAccountsActivate));
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("System Settings"));
item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
this.menu.addMenuItem(item);
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
item = new PopupMenu.PopupMenuItem(_("Lock Screen"));
item.connect('activate', Lang.bind(this, this._onLockScreenActivate));
this.menu.addMenuItem(item);
this._lockScreenItem = item;
item = new PopupMenu.PopupMenuItem(_("Switch User"));
item.connect('activate', Lang.bind(this, this._onLoginScreenActivate));
this.menu.addMenuItem(item);
this._loginScreenItem = item;
item = new PopupMenu.PopupMenuItem(_("Log Out..."));
item.connect('activate', Lang.bind(this, this._onQuitSessionActivate));
this.menu.addMenuItem(item);
this._logoutItem = item;
item = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(item);
item = new PopupMenu.PopupAlternatingMenuItem(_("Suspend"),
_("Power Off..."));
this.menu.addMenuItem(item);
this._suspendOrPowerOffItem = item;
item.connect('activate', Lang.bind(this, this._onSuspendOrPowerOffActivate));
this._updateSuspendOrPowerOff();
},
_updatePresenceStatus: function(item, event) {
let status;
if (item.state) {
status = GnomeSession.PresenceStatus.AVAILABLE;
} else {
status = GnomeSession.PresenceStatus.BUSY;
let [presence, s, msg] = this._accountMgr.get_most_available_presence();
let newPresence = this._statusChooser.getIMPresenceForSessionStatus(status);
if (newPresence != presence &&
newPresence == Tp.ConnectionPresenceType.BUSY)
Main.notify(_("Your chat status will be set to busy"),
_("Notifications are now disabled, including chat messages. Your online status has been adjusted to let others know that you might not see their messages."));
}
this._presence.status = status;
},
_onMyAccountActivate: function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().lookup_setting('gnome-user-accounts-panel.desktop');
app.activate();
},
_onOnlineAccountsActivate: function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().lookup_setting('gnome-online-accounts-panel.desktop');
app.activate(-1);
},
_onPreferencesActivate: function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().lookup_app('gnome-control-center.desktop');
app.activate();
},
_onLockScreenActivate: function() {
Main.overview.hide();
this._screenSaverProxy.LockRemote();
},
_onLoginScreenActivate: function() {
Main.overview.hide();
// Ensure we only move to GDM after the screensaver has activated; in some
// OS configurations, the X server may block event processing on VT switch
this._screenSaverProxy.SetActiveRemote(true, Lang.bind(this, function() {
this._userManager.goto_login_session();
}));
},
_onQuitSessionActivate: function() {
Main.overview.hide();
this._session.LogoutRemote(0);
},
_onSuspendOrPowerOffActivate: function() {
Main.overview.hide();
if (this._haveSuspend &&
this._suspendOrPowerOffItem.state == PopupMenu.PopupAlternatingMenuItemState.DEFAULT) {
// Ensure we only suspend after the screensaver has activated
this._screenSaverProxy.SetActiveRemote(true, Lang.bind(this, function() {
this._upClient.suspend_sync(null);
}));
} else {
this._session.ShutdownRemote();
}
}
});

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