Compare commits

...

420 Commits
osk ... 3.1.92

Author SHA1 Message Date
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
172 changed files with 44634 additions and 21821 deletions

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
EXTRA_DIST = \
.project \

251
NEWS
View File

@ -1,3 +1,254 @@
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,824 @@
/* -*- 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 1
typedef struct {
GDBusProxy *proxy;
} PluginData;
/* =============== public entry points =================== */
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);
if (strlen (result_str.UTF8Characters) != result_str.UTF8Length)
goto out;
result_copy = g_strdup (result_str.UTF8Characters);
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;
}
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;
gint signal_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;
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 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);
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);
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 get_errors_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);
}
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)
{
const gchar *uuid_str = uuid.UTF8Characters;
if (!uuid_is_valid (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 */);
return TRUE;
}
static gboolean
plugin_install_extension (PluginObject *obj,
NPString uuid,
NPString version_tag)
{
const gchar *uuid_str = uuid.UTF8Characters;
if (!uuid_is_valid (uuid_str))
return FALSE;
g_dbus_proxy_call (obj->proxy,
"InstallRemoteExtension",
g_variant_new ("(ss)",
uuid_str,
version_tag.UTF8Characters),
G_DBUS_CALL_FLAGS_NONE,
-1, /* timeout */
NULL, /* cancellable */
NULL, /* callback */
NULL /* user_data */);
return TRUE;
}
static gboolean
plugin_uninstall_extension (PluginObject *obj,
NPString uuid,
NPVariant *result)
{
GError *error = NULL;
GVariant *res;
const gchar *uuid_str;
uuid_str = uuid.UTF8Characters;
if (!uuid_is_valid (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);
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;
const gchar *uuid_str;
uuid_str = uuid.UTF8Characters;
if (!uuid_is_valid (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);
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;
const gchar *uuid_str;
uuid_str = uuid.UTF8Characters;
if (!uuid_is_valid (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);
if (!res)
{
g_warning ("Failed to retrieve errors: %s", error->message);
g_error_free (error);
return FALSE;
}
return jsonify_variant (res, result);
}
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:
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);
}
return TRUE;
}
static bool
plugin_object_has_property (NPObject *npobj,
NPIdentifier name)
{
return (name == onextension_changed_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);
}
return TRUE;
}
static bool
plugin_object_set_property (NPObject *npobj,
NPIdentifier name,
const NPVariant *value)
{
PluginObject *obj;
if (!plugin_object_has_property (npobj, name))
return FALSE;
if (name == onextension_changed_id)
{
obj = (PluginObject*) npobj;
if (obj->listener)
funcs.releaseobject (obj->listener);
obj->listener = NULL;
if (NPVARIANT_IS_OBJECT (*value))
{
obj->listener = NPVARIANT_TO_OBJECT (*value);
funcs.retainobject (obj->listener);
return TRUE;
}
else if (NPVARIANT_IS_NULL (*value))
return TRUE;
}
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");
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;
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.1.92],[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.10 dist-xz no-dist-gzip foreign])
AM_MAINTAINER_MODE([enable])
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
@ -66,14 +66,15 @@ 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
GJS_MIN_VERSION=1.29.15
MUTTER_MIN_VERSION=3.0.0
FOLKS_MIN_VERSION=0.5.2
GTK_MIN_VERSION=3.0.0
GIO_MIN_VERSION=2.25.9
GIO_MIN_VERSION=2.29.10
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.5
TELEPATHY_LOGGER_MIN_VERSION=0.2.4
POLKIT_MIN_VERSION=0.100
STARTUP_NOTIFICATION_MIN_VERSION=0.11
@ -82,9 +83,10 @@ STARTUP_NOTIFICATION_MIN_VERSION=0.11
PKG_CHECK_MODULES(GNOME_SHELL, gio-2.0 >= $GIO_MIN_VERSION
gio-unix-2.0 dbus-glib-1 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 gconf-2.0
gdk-x11-3.0 libsoup-2.4
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
@ -93,24 +95,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
@ -156,6 +155,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`
@ -200,12 +210,41 @@ 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)
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
js/Makefile
js/misc/config.js
src/Makefile
browser-plugin/Makefile
tests/Makefile
po/Makefile.in
man/Makefile

View File

@ -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 \

View File

@ -11,15 +11,6 @@
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>
@ -40,7 +31,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
@ -62,6 +53,7 @@
<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 +67,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">

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;
}

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

@ -0,0 +1,166 @@
/* 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-prompt-layout {
padding-bottom: 64px;
}
.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-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;
}

View File

@ -93,12 +93,12 @@ StTooltip StLabel {
/* PopupMenu */
.popup-menu-boxpointer {
-arrow-border-radius: 9px;
-arrow-border-radius: 8px;
-arrow-background-color: rgba(0,0,0,0.9);
-arrow-border-width: 2px;
-arrow-border-color: #5f5f5f;
-arrow-base: 30px;
-arrow-rise: 15px;
-arrow-border-color: #a5a5a5;
-arrow-base: 24px;
-arrow-rise: 11px;
}
.popup-menu {
@ -139,6 +139,15 @@ StTooltip StLabel {
border-width: 0px;
}
.popup-combo-menu {
background-color: rgba(0,0,0,0.9);
padding: 1em 0em;
color: #ffffff;
font-size: 10.5pt;
border: 1px solid #5f5f5f;
border-radius: 9px;
}
/* The remaining popup-menu sizing is all done in ems, so that if you
* override .popup-menu.font-size, everything else will scale with it.
*/
@ -155,9 +164,17 @@ StTooltip StLabel {
background-color: #4c4c4c;
}
.popup-menu-item:insensitive {
color: #9f9f9f;
}
.popup-image-menu-item {
}
.popup-combobox-item {
spacing: 1em;
}
.popup-separator-menu-item {
-gradient-height: 2px;
-gradient-start: rgba(8,8,8,0);
@ -223,6 +240,39 @@ StTooltip StLabel {
spacing: .5em;
}
/* Shared button properties */
.dash-search-button, .notification-button, .notification-icon-button,
.hotplug-notification-item, .hotplug-resident-eject-button,
.modal-dialog-button {
color: white;
border: 1px solid #8b8b8b;
background-gradient-direction: vertical;
background-gradient-start: rgba(255, 255, 255, 0.2);
background-gradient-end: rgba(255, 255, 255, 0);
}
.dash-search-button:hover, .notification-button:hover,
.notification-icon-button:hover, .hotplug-notification-item:hover,
.hotplug-resident-eject-button:hover, .modal-dialog-button:hover {
background-gradient-start: rgba(255, 255, 255, 0.3);
background-gradient-end: rgba(255, 255, 255, 0.1);
}
.dash-search-button:selected, .notification-button:focus,
.notification-icon-button:focus, .hotplug-notification-item:focus,
.modal-dialog-button:focus {
border: 2px solid #8b8b8b;
}
.dash-search-button:active, .dash-search-button:pressed,
.notification-button:active, .notification-icon-button:active,
.hotplug-notification-item:active, .hotplug-resident-eject-button:active,
.modal-dialog-button:active, .modal-dialog-button:pressed {
background-gradient-start: rgba(255, 255, 255, 0);
background-gradient-end: rgba(255, 255, 255, 0.2);
}
/* Panel */
#panel {
@ -230,12 +280,12 @@ StTooltip StLabel {
background-color: black;
border-image: url("panel-border.svg") 1;
font-size: 10.5pt;
font-weight: bold;
height: 1.86em;
}
#panelLeft, #panelCenter, #panelRight {
#panelLeft, #panelCenter {
spacing: 4px;
font-weight: bold;
}
#panelLeft:ltr {
@ -289,7 +339,8 @@ StTooltip StLabel {
}
.panel-button {
padding: 0px 12px;
-natural-hpadding: 12px;
-minimum-hpadding: 6px;
font-weight: bold;
color: #ccc;
transition-duration: 100;
@ -309,9 +360,9 @@ StTooltip StLabel {
text-shadow: black 0px 2px 2px;
}
#statusTray > .panel-button:active,
#statusTray > .panel-button:checked,
#statusTray > .panel-button:focus {
.panel-status-button:active,
.panel-status-button:checked,
.panel-status-button:focus {
background-image: url("panel-button-highlight-narrow.svg");
}
@ -321,34 +372,56 @@ StTooltip StLabel {
icon-shadow: black 0px 2px 2px;
}
/* The rounded panel corners we draw don't
* support transitions, so disable transitions
* for the buttons at the left/right edges
*/
#panelActivities {
transition-duration: 0;
.panel-menu {
-boxpointer-gap: 4px
}
#panelStatus {
transition-duration: 0;
}
#panelStatusMenu {
#panelUserMenu {
spacing: 4px;
}
#legacyTray {
spacing: 14px;
padding-left: 14px;
.status-chooser {
spacing: .4em;
}
#legacyTray:rtl {
padding-left: 0px;
padding-right: 14px;
.status-chooser .popup-menu-item,
.status-chooser-combo .popup-menu-item {
padding: .4em;
}
#legacyTray:compact {
spacing: 8px;
.status-chooser-user-icon {
border: 2px solid #8b8b8b;
border-radius: 5px;
width: 48pt;
height: 48pt;
}
.status-chooser-user-icon:hover {
border: 2px solid #bbbbbb;
}
.status-chooser-user-name {
font-weight: bold;
font-size: 1.3em;
min-width: 120pt;
}
.status-chooser-combo {
border: 1px solid transparent;
}
.status-chooser-combo.popup-combo-menu {
background-color: rgba(0,0,0,0.7);
padding: .4em 0em;
border-radius: 4px;
border: 1px solid #5f5f5f;
color: #ffffff;
font-size: 10.5pt;
}
.status-chooser-status-item,
.status-chooser-combo .popup-combobox-item {
spacing: .4em;
}
.system-status-icon {
@ -359,7 +432,6 @@ StTooltip StLabel {
#overview {
spacing: 12px;
background-color: rgba(0,0,0,0.6);
}
.window-caption {
@ -439,8 +511,6 @@ StTooltip StLabel {
.dash-placeholder {
background-image: url("dash-placeholder.svg");
height: 27px;
width: 48px;
}
#viewSelector {
@ -552,27 +622,26 @@ StTooltip StLabel {
spacing: 12px;
}
/* Text labels are an odd number of pixels tall. The uneven top and bottom
* padding compensates for this and ensures that the label is vertically
* centered */
.dash-search-button {
background-gradient-direction: vertical;
background-gradient-start: rgba(255, 255, 255, 0.2);
background-gradient-end: rgba(255, 255, 255, 0);
border: 1px solid #808080;
border-radius: 16px;
height: 32px;
padding-top: 4px;
padding-bottom: 5px;
width: 300px;
font-weight: bold;
}
.dash-search-button:selected,
.dash-search-button:hover {
background-gradient-direction: vertical;
background-gradient-start: rgba(255, 255, 255, 0.4);
background-gradient-end: rgba(255, 255, 255, 0.2);
.dash-search-button:selected {
padding-top: 3px;
padding-bottom: 4px;
width: 298px;
}
.dash-search-button-label {
color: #cccccc;
font-size: 12pt;
color: white;
font-size: 11pt;
}
/* Apps */
@ -582,6 +651,11 @@ StTooltip StLabel {
-shell-grid-item-size: 118px;
}
.contact-grid {
spacing: 36px;
-shell-grid-item-size: 272px; /* 2 * -shell-grid-item-size + spacing */
}
.icon-grid .overview-icon {
icon-size: 96px;
}
@ -648,11 +722,57 @@ StTooltip StLabel {
text-align: center;
}
.contact {
width: 272px; /* Same width as two normal results + spacing */
height: 118px; /* Aspect ratio = 1.75. Normal US business card ratio */
border-radius: 4px;
padding: 3px;
border: 1px rgba(0,0,0,0);
transition-duration: 100;
}
.contact-content {
border-radius: 2px;
padding: 8px;
width: 232px;
height: 84px;
background-color: white;
color: black;
text-align: center;
}
.contact-icon {
border-radius: 4px;
}
.contact-details {
padding: 6px 8px 11px 8px;
}
.contact-details-alias {
font-size: 16px;
padding-bottom: 11px;
}
.contact-details-status {
font-size: 11pt;
}
.contact-details-status-icon {
padding-right: 2px;
}
.contact:hover {
background-color: rgba(255,255,255,0.1);
transition-duration: 100;
}
.app-well-app.running > .overview-icon {
text-shadow: black 0px 2px 2px;
background-image: url("running-indicator.svg");
}
.contact:selected,
.app-well-app:selected > .overview-icon,
.search-result-content:selected > .overview-icon {
background-color: rgba(255,255,255,0.33);
@ -666,6 +786,7 @@ StTooltip StLabel {
transition-duration: 100;
}
.contact:focus,
.app-well-app:focus > .overview-icon,
.search-result-content:focus > .overview-icon {
border: 1px solid #cccccc;
@ -800,15 +921,15 @@ StTooltip StLabel {
/* Calendar popup */
#calendarArea {
/* this is the width of the entire popup */
min-width: 600px;
#calendarEventsArea {
/* this is the width of the second column of the popup */
min-width: 400px;
}
.calendar-vertical-separator {
-stipple-width: 1px;
-stipple-color: #505050;
width: 1.5em;
width: 0.3em;
}
#calendarPopup {
@ -831,13 +952,14 @@ StTooltip StLabel {
.calendar-month-label {
color: #666666;
font-size: 7.5pt;
padding: 2px;
padding-bottom: 8px;
padding-top: 8px;
font-weight: bold;
}
.calendar-change-month-back {
width: 20px;
height: 20px;
width: 18px;
height: 12px;
background-image: url("calendar-arrow-left.svg");
border-radius: 4px;
}
@ -854,8 +976,8 @@ StTooltip StLabel {
}
.calendar-change-month-forward {
width: 20px;
height: 20px;
width: 18px;
height: 12px;
background-image: url("calendar-arrow-right.svg");
border-radius: 4px;
}
@ -887,6 +1009,7 @@ StTooltip StLabel {
.calendar-day-base:hover {
background-color: #777777;
color: #fff;
}
.calendar-day-base:active {
@ -895,6 +1018,7 @@ StTooltip StLabel {
.calendar-day-heading {
color: #666666;
padding-top: 1em;
}
.calendar-week-number {
@ -957,7 +1081,8 @@ StTooltip StLabel {
font-size: 9pt;
font-weight: bold;
color: rgba(153, 153, 153, 1.0);
padding-left: 0.3em;
padding-left: 1.8em;
padding-top: 0.8em;
}
.events-day-header:rtl {
@ -1021,26 +1146,35 @@ StTooltip StLabel {
}
#notification {
font-size: 12pt;
border-radius: 5px 5px 0px 0px;
background: rgba(0,0,0,0.9);
font-size: 11pt;
border-radius: 10px 10px 0px 0px;
background: rgba(0,0,0,0.8);
padding: 8px 8px 4px 8px;
spacing-rows: 10px;
spacing-columns: 10px;
width: 34em;
}
.multi-line-notification {
#notification.multi-line-notification {
padding-bottom: 8px;
}
/* We use row-span = 2 for the image cell, which prevents its height preferences to be
taken into account during allocation, so its height ends up being limited by the height
of the content in the other rows. To avoid showing a stretched image, we set the minimum
height of the table to be ICON_SIZE + IMAGE_SIZE + spacing-rows = 24 + 125 + 10 = 159 */
.notification-with-image {
min-height: 159px;
}
.summary-boxpointer {
-arrow-border-radius: 9px;
-arrow-border-radius: 8px;
-arrow-background-color: rgba(0,0,0,0.9);
-arrow-border-width: 2px;
-arrow-border-color: #5f5f5f;
-arrow-base: 30px;
-arrow-rise: 15px;
-arrow-border-color: #a5a5a5;
-arrow-base: 24px;
-arrow-rise: 11px;
color: white;
}
.summary-boxpointer #notification {
@ -1094,45 +1228,26 @@ StTooltip StLabel {
}
#notification-actions {
spacing: 5px;
spacing: 10px;
}
.notification-button {
background-color: #3c3c3c;
padding: 2px 14px;
border-radius: 12px;
border: 1px solid #181818;
}
.notification-button:hover {
border: 1px solid #a1a1a1;
border-radius: 18px;
font-size: 11pt;
padding: 4px 42px 5px;
}
.notification-button:focus {
background-color: #666666;
}
.notification-button:active {
border: 1px solid #a1a1a1;
background-color: #2b2b2b;
padding: 3px 41px 4px;
}
.notification-icon-button {
border: 2px rgba(0,0,0,0.0);
border-radius: 5px;
padding: 5px;
}
.notification-icon-button:hover {
border: 2px rgba(161,161,161,0.7);
}
.notification-icon-button:focus {
background: rgba(192,192,192,0.7);
}
.notification-icon-button:active {
background: rgba(128,128,128,0.7);
padding: 4px;
}
.notification-icon-button > StIcon {
@ -1145,23 +1260,13 @@ StTooltip StLabel {
}
.hotplug-notification-item {
background-color: #3c3c3c;
padding: 0px 10px;
border-radius: 8px;
border: 1px solid #181818;
}
.hotplug-notification-item:hover {
border: 1px solid #a1a1a1;
padding: 2px 10px;
border-radius: 18px;
font-size: 10.5pt;
}
.hotplug-notification-item:focus {
background-color: #666666;
}
.hotplug-notification-item:active {
border: 1px solid #a1a1a1;
background-color: #2b2b2b;
padding: 1px 71px 1px 11px;
}
.hotplug-notification-item-icon {
@ -1203,28 +1308,20 @@ StTooltip StLabel {
}
.hotplug-resident-eject-button {
padding: 2px;
border: 1px solid #2b2b2b;
padding: 7px;
border-radius: 5px;
color: #ccc;
}
.hotplug-resident-eject-button:hover {
color: #fff;
background-color: #2b2b2b;
border: 1px solid #a1a1a1;
}
.chat-log-message {
color: #888888;
}
.chat-received {
background-gradient-direction: horizontal;
background-gradient-start: rgba(255, 255, 255, 0.2);
background-gradient-end: rgba(255, 255, 255, 0);
.chat-group-sent, .chat-group-meta {
padding: 8px 0;
}
.chat-sent {
padding-left: 4px;
border-radius: 4px;
}
@ -1235,23 +1332,20 @@ StTooltip StLabel {
}
.chat-sent {
background-gradient-direction: horizontal;
background-gradient-start: rgba(255, 255, 255, 0);
background-gradient-end: rgba(255, 255, 255, 0.2);
padding-left: 4px;
padding-left: 18pt;
border-radius: 4px;
color: #7E7E7E;
}
.chat-sent:rtl {
padding-left: 0px;
padding-right: 4px;
padding-right: 18pt;
}
.chat-meta-message {
padding-left: 4px;
border-radius: 4px;
font-size: 10.5pt;
font-size: 9pt;
color: #bbbbbb;
}
@ -1260,24 +1354,35 @@ StTooltip StLabel {
padding-right: 4px;
}
.subscription-message {
font-style: italic;
}
#notification StEntry {
padding: 4px;
border-radius: 4px;
border: 1px solid #565656;
color: #a8a8a8;
background-color: #404040;
caret-color: #ffffff;
selected-color: black;
border: 1px solid rgba(245,245,245,0.2);
background-gradient-direction: vertical;
background-gradient-start: rgb(200,200,200);
background-gradient-end: white;
transition-duration: 300;
box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6);
caret-color: #a8a8a8;
caret-size: 1px;
}
#notification StEntry:focus {
border: 1px solid #3a3a3a;
color: #545454;
background-color: #e8e8e8;
border: 1px solid #8b8b8b;
color: #333333;
background-gradient-direction: vertical;
background-gradient-start: rgb(200,200,200);
background-gradient-end: white;
caret-color: #545454;
selection-background-color: #bcbcbc;
selected-color: #323232;
box-shadow: 0px 0px 6px 2px rgba(255,255,255,0.9);
selection-background-color: #808080;
}
/* The spacing and padding on the summary is tricky; we want to keep
@ -1501,7 +1606,7 @@ StTooltip StLabel {
border-radius: 24px;
background-color: rgba(0.0, 0.0, 0.0, 0.9);
border: 2px solid #868686;
color: #ffffff;
color: #babdb6;
padding-right: 42px;
padding-left: 42px;
@ -1514,37 +1619,21 @@ StTooltip StLabel {
}
.modal-dialog-button {
border: 1px solid #8b8b8b;
border-radius: 18px;
font-size: 10.5pt;
font-size: 11pt;
color: white;
margin-left: 10px;
margin-right: 10px;
padding-left: 32px;
padding-right: 32px;
padding-top: 8px;
padding-bottom: 8px;
background-gradient-direction: vertical;
background-gradient-start: #29323b;
background-gradient-end: #121a24;
padding: 4px 32px 5px;
}
.modal-dialog-button:active,
.modal-dialog-button:pressed {
border-color: #a5a5a5;
background-gradient-start: #121a24;
background-gradient-end: #29323b;
.modal-dialog-button:disabled {
color: rgb(60, 60, 60);
}
.modal-dialog-button:focus {
border: 2px solid #a5a5a5;
padding-left: 31px;
padding-right: 31px;
padding-top: 7px;
padding-bottom: 7px;
padding: 3px 31px 4px;
}
/* Run Dialog */
@ -1806,9 +1895,16 @@ StTooltip StLabel {
}
.polkit-dialog-password-entry {
background-color: white;
background-gradient-start: rgb(236,236,236);
background-gradient-end: white;
background-gradient-direction: vertical;
color: black;
border-radius: 5px;
border: 2px solid #555753;
}
.polkit-dialog-password-entry:focus {
border: 2px solid #3465a4;
}
.polkit-dialog-error-label {
@ -1829,6 +1925,17 @@ StTooltip StLabel {
padding-bottom: 8px;
}
.network-dialog-show-password-checkbox {
padding-top: 5px;
padding-bottom: 5px;
font-size: 10pt;
color: white;
spacing: 10px;
}
.network-dialog-secret-table {
spacing-rows: 15px;
}
/* Magnifier */
@ -1839,3 +1946,58 @@ StTooltip StLabel {
.magnifier-zoom-region.full-screen {
border-width: 0px;
}
/* On-screen Keyboard */
#keyboard {
background: rgba(0,0,0,0.8);
}
.keyboard-layout {
spacing: 10px;
padding: 10px;
}
.keyboard-row {
spacing: 15px;
}
.keyboard-key {
min-height: 30px;
min-width: 30px;
background-gradient-start: rgba(255,245,245,0.4);
background-gradient-end: rgba(105,105,105,0.1);
background-gradient-direction: vertical;
font-size: 14pt;
font-weight: bold;
border-radius: 10px;
border: 2px solid #a0a0a0;
color: white;
}
.keyboard-key:grayed {
color: #808080;
border-color: #808080;
}
.keyboard-key:checked,
.keyboard-key:hover {
background: #303030;
border: 3px solid white;
}
.keyboard-key:active {
background: #808080;
}
.keyboard-subkeys {
color: white;
padding: 5px;
-arrow-border-radius: 10px;
-arrow-background-color: #090909;
-arrow-border-width: 2px;
-arrow-border-color: white;
-arrow-base: 20px;
-arrow-rise: 10px;
-boxpointer-gap: 5px;
}

View File

@ -2,6 +2,11 @@
jsdir = $(pkgdatadir)/js
nobase_dist_js_DATA = \
gdm/batch.js \
gdm/consoleKit.js \
gdm/fingerprint.js \
gdm/loginDialog.js \
gdm/powerMenu.js \
misc/config.js \
misc/docInfo.js \
misc/fileUtils.js \
@ -20,7 +25,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 \
@ -30,6 +35,7 @@ nobase_dist_js_DATA = \
ui/environment.js \
ui/extensionSystem.js \
ui/iconGrid.js \
ui/keyboard.js \
ui/layout.js \
ui/lightbox.js \
ui/link.js \
@ -39,6 +45,7 @@ nobase_dist_js_DATA = \
ui/main.js \
ui/messageTray.js \
ui/modalDialog.js \
ui/networkAgent.js \
ui/shellMountOperation.js \
ui/notificationDaemon.js \
ui/overview.js \
@ -53,7 +60,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,6 +68,7 @@ nobase_dist_js_DATA = \
ui/status/bluetooth.js \
ui/telepathyClient.js \
ui/tweener.js \
ui/userMenu.js \
ui/viewSelector.js \
ui/windowAttentionHandler.js \
ui/windowManager.js \

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

@ -0,0 +1,228 @@
/* -*- mode: js2; js2-basic-offset: 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;
function Task() {
this._init.apply(this, arguments);
}
Task.prototype = {
_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);
function Hold() {
this._init.apply(this, arguments);
}
Hold.prototype = {
__proto__: Task.prototype,
_init: function() {
Task.prototype._init.call(this,
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);
function Batch() {
this._init.apply(this, arguments);
}
Batch.prototype = {
__proto__: Task.prototype,
_init: function(scope, tasks) {
Task.prototype._init.call(this);
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);
function ConcurrentBatch() {
this._init.apply(this, arguments);
}
ConcurrentBatch.prototype = {
__proto__: Batch.prototype,
_init: function(scope, tasks) {
Batch.prototype._init.call(this, scope, tasks);
},
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);
function ConsecutiveBatch() {
this._init.apply(this, arguments);
}
ConsecutiveBatch.prototype = {
__proto__: Batch.prototype,
_init: function(scope, tasks) {
Batch.prototype._init.call(this, scope, tasks);
},
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);

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

@ -0,0 +1,32 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
const DBus = imports.dbus;
const ConsoleKitManagerIface = {
name: 'org.freedesktop.ConsoleKit.Manager',
methods: [{ name: 'CanRestart',
inSignature: '',
outSignature: 'b' },
{ name: 'CanStop',
inSignature: '',
outSignature: 'b' },
{ name: 'Restart',
inSignature: '',
outSignature: '' },
{ name: 'Stop',
inSignature: '',
outSignature: '' }]
};
function ConsoleKitManager() {
this._init();
};
ConsoleKitManager.prototype = {
_init: function() {
DBus.system.proxifyObject(this,
'org.freedesktop.ConsoleKit',
'/org/freedesktop/ConsoleKit/Manager');
}
};
DBus.proxifyPrototype(ConsoleKitManager.prototype, ConsoleKitManagerIface);

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

@ -0,0 +1,26 @@
// -*- mode: js2; indent-tabs-mode: nil; js2-basic-offset: 4 -*-
const DBus = imports.dbus;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const FprintManagerIface = {
name: 'net.reactivated.Fprint.Manager',
methods: [{ name: 'GetDefaultDevice',
inSignature: '',
outSignature: 'o' }]
};
function FprintManager() {
this._init();
};
FprintManager.prototype = {
_init: function() {
DBus.system.proxifyObject(this,
'net.reactivated.Fprint',
'/net/reactivated/Fprint/Manager');
}
};
DBus.proxifyPrototype(FprintManager.prototype, FprintManagerIface);

1369
js/gdm/loginDialog.js Normal file

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,146 @@
/* -*- mode: js2; js2-basic-offset: 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;
function PowerMenuButton() {
this._init();
}
PowerMenuButton.prototype = {
__proto__: PanelMenu.SystemStatusButton.prototype,
_init: function() {
PanelMenu.SystemStatusButton.prototype._init.call(this, '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

@ -7,4 +7,5 @@ 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@';

View File

@ -20,3 +20,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.TypeType.DIRECTORY)
recursivelyDeleteDir(child);
}
deleteGFile(dir);
}

View File

@ -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;
}

View File

@ -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

@ -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,6 +31,18 @@ function mod(a, b) {
return (a + b) % b;
}
function primaryModifier(mask) {
if (mask == 0)
return 0;
let primary = 1;
while (mask > 1) {
mask >>= 1;
primary <<= 1;
}
return primary;
}
function AltTabPopup() {
this._init();
}
@ -48,11 +60,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,9 +134,9 @@ AltTabPopup.prototype = {
}
},
show : function(backward, binding) {
let tracker = Shell.WindowTracker.get_default();
let apps = tracker.get_running_apps ('');
show : function(backward, binding, mask) {
let appSys = Shell.AppSystem.get_default();
let apps = appSys.get_running ();
if (!apps.length)
return false;
@ -130,6 +144,7 @@ AltTabPopup.prototype = {
if (!Main.pushModal(this.actor))
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));
@ -178,21 +193,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 +235,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 +270,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 +407,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 +495,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,
@ -632,11 +650,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();

View File

@ -3,6 +3,7 @@
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;
@ -35,8 +36,7 @@ AlphabeticalView.prototype = {
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 +63,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,52 +102,33 @@ 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);
}
}
};
@ -171,23 +149,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 +180,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();
@ -291,60 +304,7 @@ AllAppDisplay.prototype = {
},
_redisplay: function() {
let apps = this._appSystem.get_flattened_apps().filter(function(app) {
return !app.get_is_nodisplay();
});
this._appView.refresh(apps);
}
};
function BaseAppSearchProvider() {
this._init();
}
BaseAppSearchProvider.prototype = {
__proto__: Search.SearchProvider.prototype,
_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,
'name': app.get_name(),
'createIcon': function(size) {
return app.create_icon_texture(size);
}
};
},
activateResult: function(id, params) {
params = Params.parse(params, { workspace: null,
timestamp: null });
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);
else
app.activate(workspace);
},
dragActivateResult: function(id, params) {
params = Params.parse(params, { workspace: null,
timestamp: null });
let app = this._appSys.get_app(id);
app.open_new_window(params.workspace ? params.workspace.index() : -1);
this._appView.refresh();
}
};
@ -353,44 +313,104 @@ function AppSearchProvider() {
}
AppSearchProvider.prototype = {
__proto__: BaseAppSearchProvider.prototype,
__proto__: Search.SearchProvider.prototype,
_init: function() {
BaseAppSearchProvider.prototype._init.call(this, _("APPLICATIONS"));
Search.SearchProvider.prototype._init.call(this, _("APPLICATIONS"));
this._appSys = Shell.AppSystem.get_default();
},
getResultMeta: function(app) {
return { 'id': app,
'name': app.get_name(),
'createIcon': function(size) {
return app.create_icon_texture(size);
}
};
},
getInitialResultSet: function(terms) {
return this._appSys.initial_search(false, terms);
return this._appSys.initial_search(terms);
},
getSubsearchResultSet: function(previousResults, terms) {
return this._appSys.subsearch(false, previousResults, terms);
return this._appSys.subsearch(previousResults, terms);
},
activateResult: function(app, params) {
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
let event = Clutter.get_current_event();
let modifiers = event ? Shell.get_event_state(event) : 0;
let openNewWindow = modifiers & Clutter.ModifierType.CONTROL_MASK;
if (openNewWindow)
app.open_new_window(params.workspace);
else
app.activate_full(params.workspace, params.timestamp);
},
dragActivateResult: function(id, params) {
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
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() {
function SettingsSearchProvider() {
this._init();
}
PrefsSearchProvider.prototype = {
__proto__: BaseAppSearchProvider.prototype,
SettingsSearchProvider.prototype = {
__proto__: Search.SearchProvider.prototype,
_init: function() {
BaseAppSearchProvider.prototype._init.call(this, _("SETTINGS"));
Search.SearchProvider.prototype._init.call(this, _("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;
}
};
@ -416,12 +436,12 @@ AppIcon.prototype = {
}
};
function AppWellIcon(app, iconParams) {
this._init(app, iconParams);
function AppWellIcon(app, iconParams, onActivateOverride) {
this._init(app, iconParams, onActivateOverride);
}
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 +456,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));
@ -537,7 +559,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();
}));
@ -569,24 +591,28 @@ AppWellIcon.prototype = {
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
@ -609,7 +635,7 @@ AppIconMenu.prototype = {
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);
PopupMenu.PopupMenu.prototype._init.call(this, source.actor, 0.5, side);
// We want to keep the item hovered while the menu is up
this.blockSourceEvents = true;
@ -617,7 +643,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 +675,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 +706,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;

View File

@ -28,7 +28,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 +65,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 +84,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,8 +117,8 @@ 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);
}));
}

View File

@ -23,8 +23,6 @@ const AutorunSetting = {
ASK: 3
};
const HOTPLUG_ICON_SIZE = 16;
// misc utils
function ignoreAutorunForMount(mount) {
let root = mount.get_root();
@ -284,7 +282,7 @@ AutorunResidentSource.prototype = {
this._mounts = [];
this._notification = new AutorunResidentNotification(this);
this._setSummaryIcon(this.createNotificationIcon(HOTPLUG_ICON_SIZE));
this._setSummaryIcon(this.createNotificationIcon());
},
addMount: function(mount, apps) {
@ -329,9 +327,10 @@ 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 });
}
}
@ -532,16 +531,16 @@ AutorunTransientSource.prototype = {
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 });
}
}

View File

@ -180,7 +180,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._alignment);
},
_drawBorder: function(area) {
@ -306,19 +306,18 @@ 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._reposition(sourceActor, gap, alignment);
this._reposition(sourceActor, alignment);
},
_reposition: function(sourceActor, gap, alignment) {
_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());
@ -338,6 +337,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) {

View File

@ -351,17 +351,16 @@ function Calendar(eventSource) {
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 +368,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 +556,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,7 +612,8 @@ 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);
}
};
@ -638,17 +631,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 +650,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);;

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);

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

@ -0,0 +1,179 @@
/* -*- mode: js2; js2-basic-offset: 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 */
function Contact(id) {
this._init(id);
}
Contact.prototype = {
_init: function(id) {
this.individual = Shell.ContactSystem.get_default().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 aliasText = this.individual.alias || _("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;
default:
text = _("Offline");
iconName = 'user-offline';
}
let icon = new St.Icon({ icon_name: iconName,
icon_type: St.IconType.FULLCOLOR,
icon_size: 16,
style_class: 'contact-details-status-icon' });
let label = new St.Label({ text: text });
let box = new St.BoxLayout({ vertical: false,
style_class: 'contact-details-status' });
box.add(icon, { x_fill: true,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.START });
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 */
function ContactSearchProvider() {
this._init();
}
ContactSearchProvider.prototype = {
__proto__: Search.SearchProvider.prototype,
_init: function() {
Search.SearchProvider.prototype._init.call(this, _("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

@ -94,7 +94,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,7 +123,16 @@ 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;
}));
}
}
};
@ -147,6 +156,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);
@ -183,10 +193,11 @@ CtrlAltTabPopup.prototype = {
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 +211,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 +257,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();

View File

@ -207,7 +207,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);
@ -275,7 +275,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));
@ -330,7 +330,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
@ -437,6 +437,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++) {
@ -470,10 +471,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 &&
@ -619,12 +617,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 +684,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 +704,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;
}

View File

@ -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';
@ -39,28 +41,28 @@ function _onVertSepRepaint (area)
};
function DateMenuButton() {
this._init();
this._init.apply(this, arguments);
}
DateMenuButton.prototype = {
__proto__: PanelMenu.Button.prototype,
_init: function() {
_init: function(params) {
params = Params.parse(params, { showEvents: true });
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._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
@ -73,43 +75,58 @@ DateMenuButton.prototype = {
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 +159,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 +178,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 +191,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 +217,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

@ -30,11 +30,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) {

View File

@ -22,8 +22,8 @@ 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 GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Pango = imports.gi.Pango;
@ -60,8 +60,16 @@ const logoutDialogContent = {
subjectWithUser: _("Log Out %s"),
subject: _("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") }],
@ -71,7 +79,11 @@ const logoutDialogContent = {
const shutdownDialogContent = {
subject: _("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") },
@ -84,7 +96,11 @@ const shutdownDialogContent = {
const restartDialogContent = {
subject: _("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") }],
@ -113,7 +129,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;
@ -173,7 +189,7 @@ ListItem.prototype = {
_onClicked: function() {
this.emit('activate');
this._app.activate(-1);
this._app.activate();
}
};
Signals.addSignalMethods(ListItem.prototype);
@ -237,7 +253,7 @@ EndSessionDialog.prototype = {
_init: function() {
ModalDialog.ModalDialog.prototype._init.call(this, { 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;
@ -388,14 +404,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;
}

View File

@ -1,17 +1,31 @@
/* -*- mode: js2; js2-basic-offset: 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 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,
// Used as an error state for operations on unknown extensions,
// should never be in a real extensionMeta object.
UNINSTALLED: 99
};
const ExtensionType = {
@ -19,16 +33,54 @@ const ExtensionType = {
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/';
const _httpSession = new Soup.SessionAsync();
// 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;
}
_httpSession.ssl_ca_file = _getCertFile();
// Maps uuid -> metadata object
const extensionMeta = {};
// Maps uuid -> importer object (extension directory tree)
const extensions = {};
// Maps uuid -> extension state object (returned from init())
const extensionStateObjs = {};
// Arrays of uuids
var disabledExtensions;
var enabledExtensions;
// GFile for user extensions
var userExtensionsDir = null;
// 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);
// UUID => Array of error messages
var errors = {};
const ENABLED_EXTENSIONS_KEY = 'enabled-extensions';
/**
* versionCheck:
* @required: an array of versions we're compatible with
@ -59,13 +111,156 @@ function versionCheck(required, current) {
return false;
}
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 meta = extensionMeta[uuid];
if (!meta)
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 (meta.type != ExtensionType.PER_USER)
return false;
meta.state = ExtensionState.UNINSTALLED;
_signals.emit('extension-state-changed', meta);
delete extensionMeta[uuid];
// Importers are marked as PERMANENT, so we can't do this.
// delete extensions[uuid];
extensions[uuid] = undefined;
delete extensionStateObjs[uuid];
delete errors[uuid];
FileUtils.recursivelyDeleteDir(Gio.file_new_for_path(meta.path));
return true;
}
function gotExtensionZipFile(session, message, uuid) {
if (message.status_code != Soup.KnownStatusCode.OK) {
logExtensionError(uuid, 'downloading extension: ' + message.status_code);
return;
}
// FIXME: use a GFile mkstemp-type method once one exists
let fd, tmpzip;
try {
[fd, tmpzip] = GLib.file_open_tmp('XXXXXX.shell-extension.zip');
} catch (e) {
logExtensionError(uuid, 'tempfile: ' + e.toString());
return;
}
let stream = new Gio.UnixOutputStream({ fd: fd });
let dir = 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;
}
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, true, ExtensionType.PER_USER);
});
}
function disableExtension(uuid) {
let meta = extensionMeta[uuid];
if (!meta)
return;
if (meta.state != ExtensionState.ENABLED)
return;
let extensionState = extensionStateObjs[uuid];
try {
extensionState.disable();
} catch(e) {
logExtensionError(uuid, e.toString());
return;
}
meta.state = ExtensionState.DISABLED;
_signals.emit('extension-state-changed', meta);
}
function enableExtension(uuid) {
let meta = extensionMeta[uuid];
if (!meta)
return;
if (meta.state != ExtensionState.DISABLED)
return;
let extensionState = extensionStateObjs[uuid];
try {
extensionState.enable();
} catch(e) {
logExtensionError(uuid, e.toString());
return;
}
meta.state = ExtensionState.ENABLED;
_signals.emit('extension-state-changed', meta);
}
function logExtensionError(uuid, message, state) {
if (!errors[uuid]) errors[uuid] = [];
errors[uuid].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, enabled, type) {
let info;
let baseErrorString = 'While loading extension from "' + dir.get_parse_name() + '": ';
let uuid = dir.get_basename();
let metadataFile = dir.get_child('metadata.json');
if (!metadataFile.query_exists(null)) {
global.logError(baseErrorString + 'Missing metadata.json');
logExtensionError(uuid, 'Missing metadata.json');
return;
}
@ -73,61 +268,65 @@ function loadExtension(dir, enabled, type) {
try {
metadataContents = Shell.get_file_contents_utf8_sync(metadataFile.get_path());
} catch (e) {
global.logError(baseErrorString + 'Failed to load metadata.json: ' + e);
logExtensionError(uuid, 'Failed to load metadata.json: ' + e);
return;
}
let meta;
try {
meta = JSON.parse(metadataContents);
} catch (e) {
global.logError(baseErrorString + 'Failed to parse metadata.json: ' + e);
logExtensionError(uuid, 'Failed to parse metadata.json: ' + e);
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');
logExtensionError(uuid, 'missing "' + prop + '" property in metadata.json');
return;
}
}
if (extensions[meta.uuid] != undefined) {
global.logError(baseErrorString + "extension already loaded");
if (extensions[uuid] != undefined) {
logExtensionError(uuid, "extension already loaded");
return;
}
// Encourage people to add this
if (!meta['url']) {
global.log(baseErrorString + 'Warning: Missing "url" property in metadata.json');
global.log('Warning: Missing "url" property in metadata.json');
}
let base = dir.get_basename();
if (base != meta.uuid) {
global.logError(baseErrorString + 'uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + base + '"');
if (uuid != meta.uuid) {
logExtensionError(uuid, 'uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + uuid + '"');
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');
logExtensionError(uuid, 'extension is not compatible with current GNOME Shell and/or GJS version');
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;
return;
}
extensionMeta[uuid] = meta;
meta.type = type;
meta.path = dir.get_path();
meta.error = '';
// Default to error, we set success as the last step
extensionMeta[meta.uuid].state = ExtensionState.ERROR;
meta.state = ExtensionState.ERROR;
if (!versionCheck(meta['shell-version'], Config.PACKAGE_VERSION) ||
(meta['js-version'] && !versionCheck(meta['js-version'], Config.GJS_VERSION))) {
logExtensionError(uuid, 'extension is not compatible with current GNOME Shell and/or GJS version', ExtensionState.OUT_OF_DATE);
meta.state = ExtensionState.OUT_OF_DATE;
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,48 +337,94 @@ 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;
} 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(meta);
} 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;
if (!extensionState)
extensionState = extensionModule;
extensionStateObjs[uuid] = extensionState;
if (!extensionState.enable) {
logExtensionError(uuid, 'missing \'enable\' function');
return;
}
if (!extensionState.disable) {
logExtensionError(uuid, 'missing \'disable\' function');
return;
}
meta.state = ExtensionState.DISABLED;
if (enabled)
enableExtension(uuid);
_signals.emit('extension-loaded', meta.uuid);
_signals.emit('extension-state-changed', meta);
global.log('Loaded extension ' + meta.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);
if (!userExtensionsDir.query_exists(null))
userExtensionsDir.make_directory_with_parents(null);
} catch (e) {
global.logError('' + e);
}
disabledExtensions = global.settings.get_strv('disabled-extensions', -1);
enabledExtensions = global.settings.get_strv('enabled-extensions', -1);
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged);
enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY);
}
function _loadExtensionsIn(dir, type) {
@ -197,11 +442,8 @@ function _loadExtensionsIn(dir, 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);
let enabled = enabledExtensions.indexOf(name) != -1;
loadExtension(child, enabled, type);
}
fileEnum.close(null);
@ -217,3 +459,73 @@ function loadExtensions() {
_loadExtensionsIn(dir, ExtensionType.SYSTEM);
}
}
function InstallExtensionDialog(uuid, version_tag, name) {
this._init(uuid, version_tag, name);
}
InstallExtensionDialog.prototype = {
__proto__: ModalDialog.ModalDialog.prototype,
_init: function(uuid, version_tag, name) {
ModalDialog.ModalDialog.prototype._init.call(this, { 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 meta = { uuid: this._uuid,
state: ExtensionState.DOWNLOADING,
error: '' };
extensionMeta[this._uuid] = meta;
_signals.emit('extension-state-changed', meta);
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());
}
};

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

@ -0,0 +1,540 @@
/* -*- mode: js2; js2-basic-offset: 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 = {
name: 'org.gnome.Caribou.Keyboard',
methods: [ { name: 'Show',
inSignature: 'u',
outSignature: ''
},
{ name: 'Hide',
inSignature: 'u',
outSignature: ''
},
{ name: 'SetCursorLocation',
inSignature: 'iiii',
outSignature: ''
},
{ name: 'SetEntryLocation',
inSignature: 'iiii',
outSignature: ''
} ],
properties: [ { name: 'Name',
signature: 's',
access: 'read' } ]
};
function Key() {
this._init.apply(this, arguments);
}
Key.prototype = {
_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);
}
}
};
function Keyboard() {
this._init.apply(this, arguments);
}
Keyboard.prototype = {
_init: function () {
DBus.session.exportObject('/org/gnome/Caribou/Keyboard', this);
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 () {
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)
this._setupKeyboard();
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() {
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));
this._createSource();
},
_onKeyFocusChanged: function () {
let focus = global.stage.key_focus;
// Showing an extended key popup will grab focus, but ignore that
if (focus && focus._extended_keys)
return;
if (focus instanceof Clutter.Text)
this.show();
else
this.hide();
},
_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 (timestamp - this._timestamp < 0)
return;
this._timestamp = timestamp;
this.show();
},
Hide: function(timestamp) {
if (timestamp - this._timestamp < 0)
return;
this._timestamp = timestamp;
this.hide();
},
SetCursorLocation: function(x, y, w, h) {
this._setLocation(x, y);
},
SetEntryLocation: function(x, y, w, h) {
this._setLocation(x, y);
},
get Name() {
return 'gnome-shell';
}
};
DBus.conformExport(Keyboard.prototype, CaribouKeyboardIface);
function KeyboardSource() {
this._init.apply(this, arguments);
}
KeyboardSource.prototype = {
__proto__: MessageTray.Source.prototype,
_init: function(keyboard) {
this._keyboard = keyboard;
MessageTray.Source.prototype._init.call(this, _("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

@ -2,13 +2,20 @@
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);
@ -21,16 +28,39 @@ LayoutManager.prototype = {
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' });
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');
@ -135,9 +224,7 @@ LayoutManager.prototype = {
},
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,6 +243,130 @@ 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);
}
};
Signals.addSignalMethods(LayoutManager.prototype);
@ -218,13 +429,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 });
this._ripple2 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0 });
this._ripple3 = new St.BoxLayout({ style_class: 'ripple-box', opacity: 0 });
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 +452,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 +480,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) {
@ -321,3 +543,423 @@ HotCorner.prototype = {
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
};
function Chrome() {
this._init.apply(this, arguments);
}
Chrome.prototype = {
_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.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();
},
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[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(proxy, 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) {
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

@ -6,6 +6,7 @@ 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;
@ -222,10 +223,9 @@ function 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 +248,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);
@ -607,6 +607,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,6 +629,7 @@ 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;
}
};
@ -639,23 +643,32 @@ Extensions.prototype = {
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 ExtensionSystem.extensionMeta)
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 = ExtensionSystem.extensionMeta[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.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) {
@ -682,6 +695,8 @@ Extensions.prototype = {
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
},
@ -692,7 +707,7 @@ Extensions.prototype = {
text: meta.name });
box.add(name, { expand: true });
let description = new St.Label({ style_class: 'lg-extension-description',
text: meta.description });
text: meta.description || 'No description' });
box.add(description, { expand: true });
let metaBox = new St.BoxLayout();
@ -751,7 +766,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);
@ -832,6 +853,8 @@ LookingGlass.prototype = {
this._history = new History.HistoryManager({ gsettingsKey: HISTORY_KEY,
entry: this._entry.clutter_text });
this._resize();
},
_updateFont: function() {
@ -906,13 +929,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 +950,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);
},
@ -962,7 +982,6 @@ LookingGlass.prototype = {
this._notebook.selectIndex(0);
this.actor.show();
this.actor.lower(Main.chrome.actor);
this._open = true;
this._history.lastItem();

View File

@ -14,12 +14,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 +27,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 +44,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 +64,94 @@ 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();
keyboard.init();
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
ExtensionSystem.init();
ExtensionSystem.loadExtensions();
let shellwm = global.window_manager;
shellwm.takeover_keybinding('panel_run_dialog');
shellwm.connect('keybinding::panel_run_dialog', function () {
getRunDialog().open();
});
shellwm.takeover_keybinding('panel_main_menu');
shellwm.connect('keybinding::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
@ -91,10 +171,6 @@ function start() {
// 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
// needs to load all the .desktop files, and ShellWindowTracker
@ -113,74 +189,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 +241,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);
@ -414,6 +460,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 +473,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
@ -438,11 +500,7 @@ function notifyError(msg, details) {
else
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,9 +582,8 @@ 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)
@ -538,9 +595,9 @@ function _globalKeyPressHandler(actor, event) {
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 +606,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 +639,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;
@ -702,7 +766,6 @@ function popModal(actor, timestamp) {
function createLookingGlass() {
if (lookingGlass == null) {
lookingGlass = new LookingGlass.LookingGlass();
lookingGlass.slaveTo(panel.actor);
}
return lookingGlass;
}

View File

@ -68,14 +68,19 @@ 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) {
@ -106,12 +111,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 +143,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 +157,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();
@ -229,9 +249,7 @@ 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)
@ -266,7 +284,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 +304,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 +322,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) {
@ -395,6 +412,8 @@ function Notification(source, title, banner, params) {
}
Notification.prototype = {
IMAGE_SIZE: 125,
_init: function(source, title, banner, params) {
this.source = source;
this.urgency = Urgency.NORMAL;
@ -411,6 +430,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 +460,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();
@ -494,7 +524,10 @@ 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();
@ -558,7 +591,9 @@ Notification.prototype = {
vscrollbar_policy: this._scrollPolicy,
hscrollbar_policy: Gtk.PolicyType.NEVER,
style_class: 'vfade' });
this._table.add(this._scrollArea, { row: 1, col: 1 });
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 +670,52 @@ 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._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 +731,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 +792,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) {
@ -1094,9 +1171,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;
@ -1165,18 +1240,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 +1261,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,17 +1296,18 @@ 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);
}
@ -1274,12 +1351,11 @@ MessageTray.prototype = {
this._summaryMotionId = 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;
@ -1314,6 +1390,7 @@ MessageTray.prototype = {
this._trayState = State.HIDDEN;
this._locked = false;
this._traySummoned = false;
this._useLongerTrayLeftTimeout = false;
this._trayLeftTimeoutId = 0;
this._pointerInTray = false;
@ -1329,10 +1406,10 @@ 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);
Main.layoutManager.trayBox.add_actor(this.actor);
this.actor.y = -1;
Main.layoutManager.trackChrome(this.actor);
Main.layoutManager.trackChrome(this._notificationBin);
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._setSizePosition));
@ -1371,22 +1448,10 @@ MessageTray.prototype = {
_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 */);
},
contains: function(source) {
@ -1431,8 +1496,11 @@ 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));
@ -1532,7 +1600,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 +1800,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);
@ -1850,7 +1923,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;
@ -1944,18 +2017,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: -1,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
@ -2043,7 +2114,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 +2242,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) {
@ -2206,6 +2277,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)
@ -2229,6 +2307,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)

View File

@ -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 = {
@ -34,10 +35,12 @@ function 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 +56,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 +89,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 +100,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 +131,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 +185,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 +252,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 +269,8 @@ ModalDialog.prototype = {
} else
this._initialKeyFocus.grab_key_focus();
this._eventBlocker.lower_bottom();
if (!this._shellReactive)
this._eventBlocker.lower_bottom();
return true;
},

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

@ -0,0 +1,407 @@
/* -*- mode: js2; js2-basic-offset: 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;
function NetworkSecretDialog() {
this._init.apply(this, arguments);
}
NetworkSecretDialog.prototype = {
__proto__: ModalDialog.ModalDialog.prototype,
_init: function(agent, requestId, connection, settingName, hints) {
ModalDialog.ModalDialog.prototype._init.call(this, { 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 });
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_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');
// FIXME: need a real checkbox here
let button = new St.Button({ button_mask: St.ButtonMask.ONE,
can_focus: true });
let checkbox = new St.BoxLayout({ vertical: false,
style_class: 'network-dialog-show-password-checkbox'
});
let _switch = new PopupMenu.Switch(false);
checkbox.add(_switch.actor);
checkbox.add(new St.Label({ text: _("Show password") }), { expand: true });
button.connect('clicked', function() {
_switch.toggle();
if (_switch.state)
secret.entry.clutter_text.set_password_char('');
else
secret.entry.clutter_text.set_password_char('\u25cf');
});
button.child = checkbox;
secretTable.add(button, { row: pos, col: 1, x_expand: true, x_fill: true, y_fill: true })
pos++;
}
}
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;
}
};
function NetworkAgent() {
this._init.apply(this, arguments);
}
NetworkAgent.prototype = {
_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

@ -119,11 +119,6 @@ NotificationDaemon.prototype = {
return new St.Icon({ icon_name: icon,
icon_type: St.IconType.FULLCOLOR,
icon_size: size });
} 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);
} else {
let stockIcon;
switch (hints.urgency) {
@ -195,6 +190,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
@ -218,14 +215,18 @@ NotificationDaemon.prototype = {
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,
@ -304,7 +305,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 +318,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 +330,34 @@ NotificationDaemon.prototype = {
clear: true });
}
if (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:
@ -461,9 +486,11 @@ Source.prototype = {
_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();
},

View File

@ -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,6 +19,7 @@ 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;
@ -96,14 +98,30 @@ ShellInfo.prototype = {
};
function Overview() {
this._init();
this._init.apply(this, arguments);
}
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;
this.workspaces = null;
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);
@ -175,37 +193,61 @@ 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());
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 +317,9 @@ Overview.prototype = {
},
setScrollAdjustment: function(adjustment, direction) {
if (this.isDummy)
return;
this._scrollAdjustment = adjustment;
if (this._scrollAdjustment == null)
this._scrollDirection = SwipeScrollDirection.NONE;
@ -467,15 +512,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 +553,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,6 +582,9 @@ 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();
@ -566,6 +616,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 +634,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 +649,9 @@ Overview.prototype = {
//
// Reverses the effect of show()
hide: function() {
if (this.isDummy)
return;
if (!this._shown)
return;
@ -608,6 +670,9 @@ Overview.prototype = {
//
// Reverses the effect of showTemporarily()
hideTemporarily: function() {
if (this.isDummy)
return;
if (!this._shownTemporarily)
return;
@ -619,6 +684,9 @@ Overview.prototype = {
},
toggle: function() {
if (this.isDummy)
return;
if (this._shown)
this.hide();
else
@ -684,6 +752,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,6 +778,9 @@ Overview.prototype = {
},
_hideDone: function() {
// Re-enable unredirection
Meta.enable_unredirect_for_screen(global.screen);
global.window_group.show();
this.workspaces.destroy();

View File

@ -16,37 +16,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
@ -237,13 +244,12 @@ AppMenuButton.prototype = {
_init: function() {
PanelMenu.Button.prototype._init.call(this, 0.0);
this._metaDisplay = global.screen.get_display();
this._startingApps = [];
this._targetApp = null;
let bin = new St.Bin({ name: 'appMenu' });
this.actor.set_child(bin);
this.actor.add_actor(bin);
this.actor.reactive = false;
this._targetIsCurrent = false;
@ -287,8 +293,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));
@ -452,7 +459,7 @@ AppMenuButton.prototype = {
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) {
@ -557,10 +564,10 @@ ActivitiesButton.prototype = {
PanelMenu.Button.prototype._init.call(this, 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 +599,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
@ -698,12 +705,110 @@ function PanelCorner(panel, side) {
}
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,20 +871,14 @@ 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);
}
};
@ -790,9 +889,8 @@ function 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 +902,217 @@ 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._leftBox.add(this._appMenu.actor);
this._menus.addMenu(this._appMenu.menu);
}
/* 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

@ -2,31 +2,117 @@
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 ButtonBox(params) {
this._init.apply(this, arguments);
};
ButtonBox.prototype = {
_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);
},
}
function Button(menuAlignment) {
this._init(menuAlignment);
}
Button.prototype = {
__proto__: ButtonBox.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 });
this.actor._delegate = this;
ButtonBox.prototype._init.call(this, { 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 = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP);
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.chrome.addActor(this.menu.actor, { affectsStruts: false });
Main.uiGroup.add_actor(this.menu.actor);
this.menu.actor.hide();
},
@ -79,8 +165,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:
*
@ -100,7 +196,8 @@ SystemStatusButton.prototype = {
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);
},

View File

@ -60,13 +60,13 @@ PlaceInfo.prototype = {
// 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;
@ -118,9 +118,9 @@ 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"));
}
}
};

View File

@ -23,10 +23,10 @@
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 Gio = imports.gi.Gio;
const Mainloop = imports.mainloop;
const Polkit = imports.gi.Polkit;
@ -92,7 +92,7 @@ AuthenticationDialog.prototype = {
let 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));
@ -142,6 +142,7 @@ AuthenticationDialog.prototype = {
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 +187,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() {

View File

@ -15,6 +15,17 @@ const Tweener = imports.ui.tweener;
const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */
function _ensureStyle(actor) {
if (actor.get_children) {
let children = actor.get_children();
for (let i = 0; i < children.length; i++)
_ensureStyle(children[i]);
}
if (actor instanceof St.Widget)
actor.ensure_style();
}
function PopupBaseMenuItem(params) {
this._init(params);
}
@ -24,6 +35,7 @@ PopupBaseMenuItem.prototype = {
params = Params.parse (params, { reactive: true,
activate: true,
hover: true,
sensitive: true,
style_class: null
});
this.actor = new Shell.GenericContainer({ style_class: 'popup-menu-item',
@ -41,11 +53,15 @@ PopupBaseMenuItem.prototype = {
this._columnWidths = null;
this._spacing = 0;
this.active = false;
this._activatable = params.reactive && params.activate;
this.sensitive = this._activatable && params.sensitive;
this.setSensitive(this.sensitive);
if (params.style_class)
this.actor.add_style_class_name(params.style_class);
if (params.reactive && params.activate) {
if (this._activatable) {
this.actor.connect('button-release-event', Lang.bind(this, this._onButtonReleaseEvent));
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
}
@ -106,6 +122,23 @@ PopupBaseMenuItem.prototype = {
}
},
setSensitive: function(sensitive) {
if (!this._activatable)
return;
if (this.sensitive == sensitive)
return;
this.sensitive = sensitive;
this.actor.reactive = sensitive;
this.actor.can_focus = sensitive;
if (sensitive)
this.actor.remove_style_pseudo_class('insensitive');
else
this.actor.add_style_pseudo_class('insensitive');
this.emit('sensitive-changed', sensitive);
},
destroy: function() {
this.actor.destroy();
this.emit('destroy');
@ -203,7 +236,7 @@ PopupBaseMenuItem.prototype = {
let child = this._children[i];
if (i > 0)
width += this._spacing;
let [min, natural] = child.actor.get_preferred_width(forHeight);
let [min, natural] = child.actor.get_preferred_width(-1);
width += natural;
}
}
@ -211,10 +244,25 @@ PopupBaseMenuItem.prototype = {
},
_getPreferredHeight: function(actor, forWidth, alloc) {
let height = 0;
let height = 0, x = 0, minWidth, childWidth;
for (let i = 0; i < this._children.length; i++) {
let child = this._children[i];
let [min, natural] = child.actor.get_preferred_height(-1);
if (this._columnWidths) {
if (child.span == -1) {
childWidth = 0;
for (let j = i; j < this._columnWidths.length; j++)
childWidth += this._columnWidths[j]
} else
childWidth = this._columnWidths[i];
} else {
if (child.span == -1)
childWidth = forWidth - x;
else
[minWidth, childWidth] = child.actor.get_preferred_width(-1);
}
x += childWidth;
let [min, natural] = child.actor.get_preferred_height(childWidth);
if (natural > height)
height = natural;
}
@ -272,7 +320,10 @@ PopupBaseMenuItem.prototype = {
}
extraWidth = availWidth - naturalWidth;
} else {
availWidth = naturalWidth;
if (child.span == -1)
availWidth = box.x2 - x;
else
availWidth = naturalWidth;
extraWidth = 0;
}
@ -280,7 +331,7 @@ PopupBaseMenuItem.prototype = {
if (child.expand) {
childBox.x1 = x;
childBox.x2 = x + availWidth;
} else if (child.align === St.Align.CENTER) {
} else if (child.align === St.Align.MIDDLE) {
childBox.x1 = x + Math.round(extraWidth / 2);
childBox.x2 = childBox.x1 + naturalWidth;
} else if (child.align === St.Align.END) {
@ -294,7 +345,7 @@ PopupBaseMenuItem.prototype = {
if (child.expand) {
childBox.x1 = x - availWidth;
childBox.x2 = x;
} else if (child.align === St.Align.CENTER) {
} else if (child.align === St.Align.MIDDLE) {
childBox.x1 = x - Math.round(extraWidth / 2);
childBox.x2 = childBox.x1 + naturalWidth;
} else if (child.align === St.Align.END) {
@ -308,7 +359,7 @@ PopupBaseMenuItem.prototype = {
}
}
let [minHeight, naturalHeight] = child.actor.get_preferred_height(-1);
let [minHeight, naturalHeight] = child.actor.get_preferred_height(childBox.x2 - childBox.x1);
childBox.y1 = Math.round(box.y1 + (height - naturalHeight) / 2);
childBox.y2 = childBox.y1 + naturalHeight;
@ -706,7 +757,8 @@ PopupSwitchMenuItem.prototype = {
this.addActor(this.label);
this._statusBin = new St.Bin({ x_align: St.Align.END });
this.addActor(this._statusBin, { align: St.Align.END });
this.addActor(this._statusBin,
{ expand: true, span: -1, align: St.Align.END });
this._statusLabel = new St.Label({ text: '',
style_class: 'popup-inactive-menu-item'
@ -800,14 +852,58 @@ PopupMenuBase.prototype = {
this.passEvents = false;
this._activeMenuItem = null;
this._childMenus = [];
},
addAction: function(title, callback) {
var menuItem = new PopupMenuItem(title);
let menuItem = new PopupMenuItem(title);
this.addMenuItem(menuItem);
menuItem.connect('activate', Lang.bind(this, function (menuItem, event) {
callback(event);
}));
return menuItem;
},
addSettingsAction: function(title, desktopFile) {
// Don't allow user settings to get edited unless we're in a user session
if (global.session_type != Shell.SessionType.USER)
return null;
let menuItem = this.addAction(title, function() {
let app = Shell.AppSystem.get_default().lookup_setting(desktopFile);
if (!app) {
log('Settings panel for desktop file ' + desktopFile + ' could not be loaded!');
return;
}
Main.overview.hide();
app.activate();
});
return menuItem;
},
isChildMenu: function(menu) {
return this._childMenus.indexOf(menu) != -1;
},
addChildMenu: function(menu) {
if (this.isChildMenu(menu))
return;
this._childMenus.push(menu);
this.emit('child-menu-added', menu);
},
removeChildMenu: function(menu) {
let index = this._childMenus.indexOf(menu);
if (index == -1)
return;
this._childMenus.splice(index, 1);
this.emit('child-menu-removed', menu);
},
/**
@ -843,6 +939,17 @@ PopupMenuBase.prototype = {
this.emit('active-changed', null);
}
}));
menuItem._sensitiveChangeId = menuItem.connect('sensitive-changed', Lang.bind(this, function(menuItem, sensitive) {
if (!sensitive && this._activeMenuItem == menuItem) {
if (!this.actor.navigate_focus(menuItem.actor,
Gtk.DirectionType.TAB_FORWARD,
true))
this.actor.grab_key_focus();
} else if (sensitive && this._activeMenuItem == null) {
if (global.stage.get_key_focus() == this.actor)
menuItem.actor.grab_key_focus();
}
}));
menuItem._activateId = menuItem.connect('activate', Lang.bind(this, function (menuItem, event) {
this.emit('activate', menuItem);
this.close(true);
@ -850,6 +957,7 @@ PopupMenuBase.prototype = {
menuItem.connect('destroy', Lang.bind(this, function(emitter) {
menuItem.disconnect(menuItem._activateId);
menuItem.disconnect(menuItem._activeChangeId);
menuItem.disconnect(menuItem._sensitiveChangeId);
if (menuItem.menu) {
menuItem.menu.disconnect(menuItem._subMenuActivateId);
menuItem.menu.disconnect(menuItem._subMenuActiveChangeId);
@ -860,6 +968,39 @@ PopupMenuBase.prototype = {
}));
},
_updateSeparatorVisibility: function(menuItem) {
let children = this.box.get_children();
let index = children.indexOf(menuItem.actor);
if (index < 0)
return;
let childBeforeIndex = index - 1;
while (childBeforeIndex >= 0 && !children[childBeforeIndex].visible)
childBeforeIndex--;
if (childBeforeIndex < 0
|| children[childBeforeIndex]._delegate instanceof PopupSeparatorMenuItem) {
menuItem.actor.hide();
return;
}
let childAfterIndex = index + 1;
while (childAfterIndex < children.length && !children[childAfterIndex].visible)
childAfterIndex++;
if (childAfterIndex >= children.length
|| children[childAfterIndex]._delegate instanceof PopupSeparatorMenuItem) {
menuItem.actor.hide();
return;
}
menuItem.actor.show();
},
addMenuItem: function(menuItem, position) {
let before_item = null;
if (position == undefined) {
@ -891,6 +1032,14 @@ PopupMenuBase.prototype = {
if (!open)
menuItem.menu.close(false);
});
} else if (menuItem instanceof PopupSeparatorMenuItem) {
this._connectItemSignals(menuItem);
// updateSeparatorVisibility needs to get called any time the
// separator's adjacent siblings change visibility or position.
// open-state-changed isn't exactly that, but doing it in more
// precise ways would require a lot more bookkeeping.
this.connect('open-state-changed', Lang.bind(this, function() { this._updateSeparatorVisibility(menuItem); }));
} else if (menuItem instanceof PopupBaseMenuItem)
this._connectItemSignals(menuItem);
else
@ -954,6 +1103,10 @@ PopupMenuBase.prototype = {
return null;
},
get numMenuItems() {
return this._getMenuItems().length;
},
removeAll: function() {
let children = this._getMenuItems();
for (let i = 0; i < children.length; i++) {
@ -985,12 +1138,11 @@ function PopupMenu() {
PopupMenu.prototype = {
__proto__: PopupMenuBase.prototype,
_init: function(sourceActor, alignment, arrowSide, gap) {
_init: function(sourceActor, alignment, arrowSide) {
PopupMenuBase.prototype._init.call (this, sourceActor, 'popup-menu-content');
this._alignment = alignment;
this._arrowSide = arrowSide;
this._gap = gap;
this._boxPointer = new BoxPointer.BoxPointer(arrowSide,
{ x_fill: true,
@ -1048,9 +1200,11 @@ PopupMenu.prototype = {
this.isOpen = true;
this._boxPointer.setPosition(this.sourceActor, this._gap, this._alignment);
this._boxPointer.setPosition(this.sourceActor, this._alignment);
this._boxPointer.show(animate);
this.actor.raise_top();
this.emit('open-state-changed', true);
},
@ -1318,6 +1472,227 @@ PopupSubMenuMenuItem.prototype = {
}
};
function PopupComboMenu() {
this._init.apply(this, arguments);
}
PopupComboMenu.prototype = {
__proto__: PopupMenuBase.prototype,
_init: function(sourceActor) {
PopupMenuBase.prototype._init.call(this,
sourceActor, 'popup-combo-menu');
this.actor = this.box;
this.actor._delegate = this;
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
this._activeItemPos = -1;
global.focus_manager.add_group(this.actor);
},
_onKeyPressEvent: function(actor, event) {
if (event.get_key_symbol() == Clutter.Escape) {
this.close(true);
return true;
}
return false;
},
_onKeyFocusIn: function(actor) {
let items = this._getMenuItems();
let activeItem = items[this._activeItemPos];
activeItem.actor.grab_key_focus();
},
open: function() {
if (this.isOpen)
return;
this.isOpen = true;
let [sourceX, sourceY] = this.sourceActor.get_transformed_position();
let items = this._getMenuItems();
let activeItem = items[this._activeItemPos];
this.actor.set_position(sourceX, sourceY - activeItem.actor.y);
this.actor.width = Math.max(this.actor.width, this.sourceActor.width);
this.actor.raise_top();
this.actor.opacity = 0;
this.actor.show();
Tweener.addTween(this.actor,
{ opacity: 255,
transition: 'linear',
time: BoxPointer.POPUP_ANIMATION_TIME });
this.emit('open-state-changed', true);
},
close: function() {
if (!this.isOpen)
return;
this.isOpen = false;
Tweener.addTween(this.actor,
{ opacity: 0,
transition: 'linear',
time: BoxPointer.POPUP_ANIMATION_TIME,
onComplete: Lang.bind(this,
function() {
this.actor.hide();
})
});
this.emit('open-state-changed', false);
},
setActiveItem: function(position) {
this._activeItemPos = position;
},
setItemVisible: function(position, visible) {
if (!visible && position == this._activeItemPos) {
log('Trying to hide the active menu item.');
return;
}
this._getMenuItems()[position].actor.visible = visible;
},
getItemVisible: function(position) {
return this._getMenuItems()[position].actor.visible;
}
};
function PopupComboBoxMenuItem() {
this._init.apply(this, arguments);
}
PopupComboBoxMenuItem.prototype = {
__proto__: PopupBaseMenuItem.prototype,
_init: function (params) {
PopupBaseMenuItem.prototype._init.call(this, params);
this._itemBox = new Shell.Stack();
this.addActor(this._itemBox);
let expander = new St.Label({ text: '\u2304' });
this.addActor(expander, { align: St.Align.END,
span: -1 });
this._menu = new PopupComboMenu(this.actor);
Main.uiGroup.add_actor(this._menu.actor);
this._menu.actor.hide();
if (params.style_class)
this._menu.actor.add_style_class_name(params.style_class);
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
this._activeItemPos = -1;
this._items = [];
},
_getTopMenu: function() {
let actor = this.actor.get_parent();
while (actor) {
if (actor._delegate &&
(actor._delegate instanceof PopupMenu ||
actor._delegate instanceof PopupComboMenu))
return actor._delegate;
actor = actor.get_parent();
}
return null;
},
_onScrollEvent: function(actor, event) {
if (this._activeItemPos == -1)
return;
let position = this._activeItemPos;
let direction = event.get_scroll_direction();
if (direction == Clutter.ScrollDirection.DOWN) {
while (position < this._items.length - 1) {
position++;
if (this._menu.getItemVisible(position))
break;
}
} else if (direction == Clutter.ScrollDirection.UP) {
while (position > 0) {
position--;
if (this._menu.getItemVisible(position))
break;
}
}
if (position == this._activeItemPos)
return;
this.setActiveItem(position);
this.emit('active-item-changed', position);
},
activate: function(event) {
let topMenu = this._getTopMenu();
if (!topMenu)
return;
topMenu.addChildMenu(this._menu);
this._menu.toggle();
},
addMenuItem: function(menuItem, position) {
if (position === undefined)
position = this._menu.numMenuItems;
this._menu.addMenuItem(menuItem, position);
_ensureStyle(this._menu.actor);
let item = new St.BoxLayout({ style_class: 'popup-combobox-item' });
let children = menuItem.actor.get_children();
for (let i = 0; i < children.length; i++) {
let clone = new Clutter.Clone({ source: children[i] });
item.add(clone, { y_fill: false });
}
let oldItem = this._items[position];
if (oldItem)
this._itemBox.remove_actor(oldItem);
this._items[position] = item;
this._itemBox.add_actor(item);
menuItem.connect('activate',
Lang.bind(this, this._itemActivated, position));
},
setActiveItem: function(position) {
let item = this._items[position];
if (!item)
return;
if (this._activeItemPos == position)
return;
this._menu.setActiveItem(position);
this._activeItemPos = position;
for (let i = 0; i < this._items.length; i++)
this._items[i].visible = (i == this._activeItemPos);
},
setItemVisible: function(position, visible) {
this._menu.setItemVisible(position, visible);
},
_itemActivated: function(menuItem, event, position) {
this.setActiveItem(position);
this.emit('active-item-changed', position);
}
};
/* Basic implementation of a menu manager.
* Call addMenu to add menus
@ -1337,6 +1712,7 @@ PopupMenuManager.prototype = {
this._keyFocusNotifyId = 0;
this._activeMenu = null;
this._menus = [];
this._menuStack = [];
this._preGrabInputMode = null;
this._grabbedFromKeynav = false;
},
@ -1345,6 +1721,8 @@ PopupMenuManager.prototype = {
let menudata = {
menu: menu,
openStateChangeId: menu.connect('open-state-changed', Lang.bind(this, this._onMenuOpenState)),
childMenuAddedId: menu.connect('child-menu-added', Lang.bind(this, this._onChildMenuAdded)),
childMenuRemovedId: menu.connect('child-menu-removed', Lang.bind(this, this._onChildMenuRemoved)),
destroyId: menu.connect('destroy', Lang.bind(this, this._onMenuDestroy)),
enterId: 0,
focusInId: 0
@ -1372,6 +1750,8 @@ PopupMenuManager.prototype = {
let menudata = this._menus[position];
menu.disconnect(menudata.openStateChangeId);
menu.disconnect(menudata.childMenuAddedId);
menu.disconnect(menudata.childMenuRemovedId);
menu.disconnect(menudata.destroyId);
if (menudata.enterId)
@ -1409,8 +1789,20 @@ PopupMenuManager.prototype = {
},
_onMenuOpenState: function(menu, open) {
if (open)
if (open) {
if (this._activeMenu && this._activeMenu.isChildMenu(menu)) {
this._menuStack.push(this._activeMenu);
menu.actor.grab_key_focus();
}
this._activeMenu = menu;
} else {
if (this._menuStack.length > 0) {
this._activeMenu = this._menuStack.pop();
if (menu.sourceActor)
menu.sourceActor.grab_key_focus();
this._didPop = true;
}
}
// Check what the focus was before calling pushModal/popModal
let focus = global.stage.key_focus;
@ -1443,6 +1835,14 @@ PopupMenuManager.prototype = {
}
},
_onChildMenuAdded: function(menu, childMenu) {
this.addMenu(childMenu);
},
_onChildMenuRemoved: function(menu, childMenu) {
this.removeMenu(childMenu);
},
// change the currently-open menu without dropping grab
_changeMenu: function(newMenu) {
if (this._activeMenu) {
@ -1451,6 +1851,8 @@ PopupMenuManager.prototype = {
// before closing it to keep that from happening
let oldMenu = this._activeMenu;
this._activeMenu = null;
for (let i = this._menuStack.length - 1; i >= 0; i--)
this._menuStack[i].close(false);
oldMenu.close(false);
newMenu.open(false);
} else
@ -1461,6 +1863,15 @@ PopupMenuManager.prototype = {
if (!this.grabbed || menu == this._activeMenu)
return false;
if (this._activeMenu && this._activeMenu.isChildMenu(menu))
return false;
if (this._menuStack.indexOf(menu) != -1)
return false;
if (this._menuStack.length > 0 && this._menuStack[0].isChildMenu(menu))
return false;
this._changeMenu(menu);
return false;
},
@ -1473,6 +1884,8 @@ PopupMenuManager.prototype = {
if (focus) {
if (this._activeMenuContains(focus))
return;
if (this._menuStack.length > 0)
return;
if (focus._delegate && focus._delegate.menu &&
this._findMenu(focus._delegate.menu) != -1)
return;
@ -1531,6 +1944,11 @@ PopupMenuManager.prototype = {
if (this._activeMenu != null && this._activeMenu.passEvents)
return false;
if (this._didPop) {
this._didPop = false;
return true;
}
let activeMenuContains = this._eventIsOnActiveMenu(event);
let eventType = event.type();

View File

@ -112,6 +112,43 @@ function SearchProvider(title) {
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);
},
/**
@ -315,9 +352,18 @@ SearchSystem.prototype = {
},
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 +377,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 +406,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 +419,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 +431,7 @@ SearchSystem.prototype = {
this._previousTerms = terms;
this._previousResults = results;
return results;
}
this.emit('search-completed', results);
},
};
Signals.addSignalMethods(SearchSystem.prototype);

View File

@ -88,7 +88,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) {
@ -100,17 +100,17 @@ SearchResult.prototype = {
};
function GridSearchResults(provider) {
this._init(provider);
function GridSearchResults(provider, grid) {
this._init(provider, grid);
}
GridSearchResults.prototype = {
__proto__: Search.SearchResultDisplay.prototype,
_init: function(provider) {
_init: function(provider, grid) {
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);
@ -189,6 +189,8 @@ function SearchResults(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 +225,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 +294,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 +321,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 +341,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 +368,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 +380,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)

View File

@ -1,7 +1,10 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const DBus = imports.dbus;
const Lang = imports.lang;
const Config = imports.misc.config;
const ExtensionSystem = imports.ui.extensionSystem;
const Main = imports.ui.main;
const GnomeShellIface = {
@ -9,12 +12,59 @@ const GnomeShellIface = {
methods: [{ name: 'Eval',
inSignature: 's',
outSignature: 'bs'
},
{ name: 'ListExtensions',
inSignature: '',
outSignature: 'a{sa{sv}}'
},
{ name: 'GetExtensionInfo',
inSignature: 's',
outSignature: 'a{sv}'
},
{ name: 'GetExtensionErrors',
inSignature: 's',
outSignature: 'as'
},
{ name: 'ScreenshotArea',
inSignature: 'iiiis',
outSignature: 'b'
},
{ name: 'ScreenshotWindow',
inSignature: 'bs',
outSignature: 'b'
},
{ name: 'Screenshot',
inSignature: 's',
outSignature: 'b'
},
{ name: 'EnableExtension',
inSignature: 's',
outSignature: ''
},
{ name: 'DisableExtension',
inSignature: 's',
outSignature: ''
},
{ name: 'InstallRemoteExtension',
inSignature: 'ss',
outSignature: ''
},
{ name: 'UninstallExtension',
inSignature: 's',
outSignature: 'b'
}
],
signals: [],
signals: [{ name: 'ExtensionStatusChanged',
inSignature: 'sis' }],
properties: [{ name: 'OverviewActive',
signature: 'b',
access: 'readwrite' }]
access: 'readwrite' },
{ name: 'ApiVersion',
signature: 'i',
access: 'read' },
{ name: 'ShellVersion',
signature: 's',
access: 'read' }]
};
function GnomeShell() {
@ -24,6 +74,8 @@ function GnomeShell() {
GnomeShell.prototype = {
_init: function() {
DBus.session.exportObject('/org/gnome/Shell', this);
ExtensionSystem.connect('extension-state-changed',
Lang.bind(this, this._extensionStateChanged));
},
/**
@ -56,6 +108,84 @@ GnomeShell.prototype = {
return [success, returnValue];
},
/**
* 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 (x, y, width, height, filename, callback) {
global.screenshot_area (x, y, width, height, filename, function (obj, result) { callback(result); });
},
/**
* 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.
*
*/
ScreenshotWindow : function (include_frame, filename) {
return global.screenshot_window (include_frame, filename);
},
/**
* 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 (filename, callback) {
global.screenshot(filename, function (obj, result) { callback(result); });
},
ListExtensions: function() {
return ExtensionSystem.extensionMeta;
},
GetExtensionInfo: function(uuid) {
return ExtensionSystem.extensionMeta[uuid] || {};
},
GetExtensionErrors: function(uuid) {
return ExtensionSystem.errors[uuid] || [];
},
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);
},
get OverviewActive() {
return Main.overview.visible;
},
@ -65,6 +195,17 @@ GnomeShell.prototype = {
Main.overview.show();
else
Main.overview.hide();
},
ApiVersion: ExtensionSystem.API_VERSION,
ShellVersion: Config.PACKAGE_VERSION,
_extensionStateChanged: function(_, newState) {
DBus.session.emit_signal('/org/gnome/Shell',
'org.gnome.Shell',
'ExtensionStatusChanged', 'sis',
[newState.uuid, newState.state, newState.error]);
}
};

View File

@ -84,7 +84,7 @@ ListItem.prototype = {
_onClicked: function() {
this.emit('activate');
this._app.activate(-1);
this._app.activate();
}
};
Signals.addSignalMethods(ListItem.prototype);
@ -402,4 +402,4 @@ ShellProcessesDialog.prototype = {
_setButtonsForChoices(this, choices);
}
}
Signals.addSignalMethods(ShellProcessesDialog.prototype);
Signals.addSignalMethods(ShellProcessesDialog.prototype);

View File

@ -68,9 +68,9 @@ 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);
this.menu.addMenuItem(visualBell);
@ -88,11 +88,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) {

View File

@ -67,7 +67,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 +88,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 +157,6 @@ Indicator.prototype = {
this._hasDevices = true;
}
}
if (this._hasDevices)
this._deviceSep.actor.show();
else
this._deviceSep.actor.hide();
},
_updateDeviceItem: function(item, device) {
@ -277,21 +268,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 +288,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);

View File

@ -49,10 +49,11 @@ XKBIndicator.prototype = {
PanelMenu.Button.prototype._init.call(this, St.Align.START);
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 +62,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 +122,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 +147,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 +173,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 +193,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 +210,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;

View File

@ -45,17 +45,6 @@ const NM80211ApSecurityFlags = NetworkManager['80211ApSecurityFlags'];
// (the remaining are placed into More...)
const NUM_VISIBLE_NETWORKS = 5;
const NMAppletHelperInterface = {
name: 'org.gnome.network_manager_applet',
methods: [
{ name: 'ConnectToHiddenNetwork', inSignature: '', outSignature: '' },
{ name: 'CreateWifiNetwork', inSignature: '', outSignature: '' },
{ name: 'ConnectTo8021xNetwork', inSignature: 'oo', outSignature: '' },
{ name: 'ConnectTo3gNetwork', inSignature: 'o', outSignature: '' }
],
};
const NMAppletProxy = DBus.makeProxyClass(NMAppletHelperInterface);
function macToArray(string) {
return string.split(':').map(function(el) {
return parseInt(el, 16);
@ -102,6 +91,13 @@ function sortAccessPoints(accessPoints) {
});
}
function ssidToLabel(ssid) {
let label = NetworkManager.utils_ssid_to_utf8(ssid);
if (!label)
label = _("<unknown>");
return label;
}
function NMNetworkMenuItem() {
this._init.apply(this, arguments);
}
@ -117,10 +113,7 @@ NMNetworkMenuItem.prototype = {
if (!title) {
let ssid = this.bestAP.get_ssid();
if (ssid)
title = NetworkManager.utils_ssid_to_utf8(ssid);
if (!title)
title = _("<unknown>");
title = ssidToLabel(ssid);
}
this._label = new St.Label({ text: title });
@ -631,15 +624,8 @@ NMDevice.prototype = {
this.emit('network-lost');
}
switch(newstate) {
case NetworkManager.DeviceState.NEED_AUTH:
// FIXME: make this have a real effect
// (currently we rely on a running nm-applet)
this.emit('need-auth');
break;
case NetworkManager.DeviceState.FAILED:
if (newstate == NetworkManager.DeviceState.FAILED) {
this.emit('activation-failed', reason);
break;
}
this._updateStatusItem();
@ -674,7 +660,7 @@ NMDevice.prototype = {
let dev_product = this.device.get_product();
let dev_vendor = this.device.get_vendor();
if (!dev_product || !dev_vendor)
return null;
return '';
let product = Util.fixupPCIDescription(dev_product);
let vendor = Util.fixupPCIDescription(dev_vendor);
@ -748,10 +734,6 @@ NMDeviceModem.prototype = {
this.mobileDevice = null;
this._connectionType = 'ppp';
this._applet_proxy = new NMAppletProxy(DBus.session,
'org.gnome.network_manager_applet',
'/org/gnome/network_manager_applet');
this._capabilities = device.current_capabilities;
if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
is_wwan = true;
@ -853,12 +835,10 @@ NMDeviceModem.prototype = {
},
_createAutomaticConnection: function() {
// Mobile wizard is handled by nm-applet for now...
this._applet_proxy.ConnectTo3gNetworkRemote(this.device.get_path(),
Lang.bind(this, function(results, err) {
if (err)
log(err);
}));
// Mobile wizard is too complex for the shell UI and
// is handled by the network panel
Util.spawn(['gnome-control-center', 'network',
'connect-3g', this.device.get_path()]);
return null;
}
};
@ -970,10 +950,6 @@ NMDeviceWireless.prototype = {
this._overflowItem = null;
this._networks = [ ];
this._applet_proxy = new NMAppletProxy(DBus.session,
'org.gnome.network_manager_applet',
'/org/gnome/network_manager_applet');
// breaking the layers with this, but cannot call
// this.connectionValid until I have a device
this.device = device;
@ -985,6 +961,16 @@ NMDeviceWireless.prototype = {
for (let i = 0; i < accessPoints.length; i++) {
// Access points are grouped by network
let ap = accessPoints[i];
if (ap.get_ssid() == null) {
// hidden access point cannot be added, we need to know
// the SSID and security details to connect
// nevertheless, the access point can acquire a SSID when
// NetworkManager connects to it (via nmcli or the control-center)
ap._notifySsidId = ap.connect('notify::ssid', Lang.bind(this, this._notifySsidCb));
continue;
}
let pos = this._findNetwork(ap);
let obj;
if (pos != -1) {
@ -998,7 +984,7 @@ NMDeviceWireless.prototype = {
item: null,
accessPoints: [ ap ]
};
obj.ssidText = NetworkManager.utils_ssid_to_utf8(obj.ssid);
obj.ssidText = ssidToLabel(obj.ssid);
this._networks.push(obj);
}
@ -1011,8 +997,14 @@ NMDeviceWireless.prototype = {
}
}
}
if (this.device.active_access_point) {
this._activeNetwork = this._networks[this._findNetwork(this.device.active_access_point)];
let networkPos = this._findNetwork(this.device.active_access_point);
if (networkPos == -1) // the connected access point is invisible
this._activeNetwork = null;
else
this._activeNetwork = this._networks[networkPos];
} else {
this._activeNetwork = null;
}
@ -1098,6 +1090,14 @@ NMDeviceWireless.prototype = {
}
},
_notifySsidCb: function(accessPoint) {
if (accessPoint.get_ssid() != null) {
accessPoint.disconnect(accessPoint._notifySsidId);
accessPoint._notifySsidId = 0;
this._accessPointAdded(this.device, accessPoint);
}
},
_activeApChanged: function() {
this._activeNetwork = null;
@ -1105,7 +1105,9 @@ NMDeviceWireless.prototype = {
if (activeAp) {
let pos = this._findNetwork(activeAp);
this._activeNetwork = this._networks[pos];
if (pos != -1)
this._activeNetwork = this._networks[pos];
}
// we don't refresh the view here, setActiveConnection will
@ -1180,6 +1182,9 @@ NMDeviceWireless.prototype = {
},
_findNetwork: function(accessPoint) {
if (accessPoint.get_ssid() == null)
return -1;
for (let i = 0; i < this._networks.length; i++) {
if (this._networkCompare(this._networks[i], accessPoint))
return i;
@ -1188,6 +1193,13 @@ NMDeviceWireless.prototype = {
},
_accessPointAdded: function(device, accessPoint) {
if (accessPoint.get_ssid() == null) {
// This access point is not visible yet
// Wait for it to get a ssid
accessPoint._notifySsidId = accessPoint.connect('notify::ssid', Lang.bind(this, this._notifySsidCb));
return;
}
let pos = this._findNetwork(accessPoint);
let apObj;
let needsupdate = false;
@ -1210,7 +1222,7 @@ NMDeviceWireless.prototype = {
item: null,
accessPoints: [ accessPoint ]
};
apObj.ssidText = NetworkManager.utils_ssid_to_utf8(apObj.ssid);
apObj.ssidText = ssidToLabel(apObj.ssid);
needsupdate = true;
}
@ -1279,6 +1291,9 @@ NMDeviceWireless.prototype = {
apObj.accessPoints.splice(i, 1);
if (apObj.accessPoints.length == 0) {
if (this._activeNetwork == apObj)
this._activeNetwork = null;
if (apObj.item)
apObj.item.destroy();
@ -1423,13 +1438,12 @@ NMDeviceWireless.prototype = {
},
_createActiveConnectionItem: function() {
let activeAp = this.device.active_access_point;
let icon, title;
if (this._activeConnection._connection) {
let connection = this._activeConnection._connection;
if (activeAp)
this._activeConnectionItem = new NMNetworkMenuItem([ activeAp ], undefined,
{ reactive: false });
if (this._activeNetwork)
this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined,
{ reactive: false });
else
this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(connection._name,
'network-wireless-connected',
@ -1437,9 +1451,9 @@ NMDeviceWireless.prototype = {
} else {
// We cannot read the connection (due to ACL, or API incompatibility), but we still show signal if we have it
let menuItem;
if (activeAp)
this._activeConnectionItem = new NMNetworkMenuItem([ activeAp ], undefined,
{ reactive: false });
if (this._activeNetwork)
this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined,
{ reactive: false });
else
this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(_("Connected (private)"),
'network-wireless-connected',
@ -1469,6 +1483,11 @@ NMDeviceWireless.prototype = {
},
_createNetworkItem: function(apObj, position) {
if(!apObj.accessPoints || apObj.accessPoints.length == 0) {
// this should not happen, but I have no idea why it happens
return;
}
if(apObj.connections.length > 0) {
if (apObj.connections.length == 1)
apObj.item = this._createAPItem(apObj.connections[0], apObj, false);
@ -1486,13 +1505,10 @@ NMDeviceWireless.prototype = {
let accessPoints = sortAccessPoints(apObj.accessPoints);
if ( (accessPoints[0]._secType == NMAccessPointSecurity.WPA2_ENT)
|| (accessPoints[0]._secType == NMAccessPointSecurity.WPA_ENT)) {
// 802.1x-enabled APs get handled by nm-applet for now...
this._applet_proxy.ConnectTo8021xNetworkRemote(this.device.get_path(),
accessPoints[0].dbus_path,
Lang.bind(this, function(results, err) {
if (err)
log(err);
}));
// 802.1x-enabled APs require further configuration, so they're
// handled in gnome-control-center
Util.spawn(['gnome-control-center', 'network', 'connect-8021x-wifi',
this.device.get_path(), accessPoints[0].dbus_path]);
} else {
let connection = this._createAutomaticConnection(apObj);
this._client.add_and_activate_connection(connection, this.device, accessPoints[0].dbus_path, null)
@ -1550,9 +1566,9 @@ NMApplet.prototype = {
this._statusSection.addAction(_("Enable networking"), Lang.bind(this, function() {
this._client.networking_enabled = true;
}));
this._statusSection.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._statusSection.actor.hide();
this.menu.addMenuItem(this._statusSection);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices = { };
@ -1563,9 +1579,9 @@ NMApplet.prototype = {
};
this._devices.wired.section.addMenuItem(this._devices.wired.item);
this._devices.wired.section.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.wired.section.actor.hide();
this.menu.addMenuItem(this._devices.wired.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.wireless = {
section: new PopupMenu.PopupMenuSection(),
@ -1573,9 +1589,9 @@ NMApplet.prototype = {
item: this._makeToggleItem('wireless', _("Wireless"))
};
this._devices.wireless.section.addMenuItem(this._devices.wireless.item);
this._devices.wireless.section.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.wireless.section.actor.hide();
this.menu.addMenuItem(this._devices.wireless.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.wwan = {
section: new PopupMenu.PopupMenuSection(),
@ -1583,9 +1599,9 @@ NMApplet.prototype = {
item: this._makeToggleItem('wwan', _("Mobile broadband"))
};
this._devices.wwan.section.addMenuItem(this._devices.wwan.item);
this._devices.wwan.section.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.wwan.section.actor.hide();
this.menu.addMenuItem(this._devices.wwan.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.vpn = {
section: new PopupMenu.PopupMenuSection(),
@ -1598,15 +1614,10 @@ NMApplet.prototype = {
this._devices.vpn.item.updateForDevice(this._devices.vpn.device);
this._devices.vpn.section.addMenuItem(this._devices.vpn.item);
this._devices.vpn.section.addMenuItem(this._devices.vpn.device.section);
this._devices.vpn.section.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.vpn.section.actor.hide();
this.menu.addMenuItem(this._devices.vpn.section);
this.menu.addAction(_("Network Settings"), function() {
Main.overview.hide();
let app = Shell.AppSystem.get_default().get_app('gnome-network-panel.desktop');
app.activate(-1);
});
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop');
this._activeConnections = [ ];
this._connections = [ ];
@ -1660,8 +1671,7 @@ NMApplet.prototype = {
_ensureSource: function() {
if (!this._source) {
this._source = new NMMessageTraySource();
this._source._destroyId = this._source.connect('destroy', Lang.bind(this, function() {
this._source._destroyId = 0;
this._source.connect('destroy', Lang.bind(this, function() {
this._source = null;
}));
Main.messageTray.add(this._source);
@ -1709,6 +1719,28 @@ NMApplet.prototype = {
}
},
_notifyForDevice: function(device, iconName, title, text, urgency) {
if (device._notification)
device._notification.destroy();
/* must call after destroying previous notification,
or this._source will be cleared */
this._ensureSource();
let icon = new St.Icon({ icon_name: iconName,
icon_type: St.IconType.SYMBOLIC,
icon_size: this._source.ICON_SIZE
});
device._notification = new MessageTray.Notification(this._source, title, text,
{ icon: icon });
device._notification.setUrgency(urgency);
device._notification.setTransient(true);
device._notification.connect('destroy', function() {
device._notification = null;
});
this._source.notify(device._notification);
},
_deviceAdded: function(client, device) {
if (device._delegate) {
// already seen, not adding again
@ -1718,42 +1750,21 @@ NMApplet.prototype = {
if (wrapperClass) {
let wrapper = new wrapperClass(this._client, device, this._connections);
// FIXME: these notifications are duplicate with those exposed by nm-applet
// uncomment this code in 3.2, when we'll conflict with and kill nm-applet
/* wrapper._networkLostId = wrapper.connect('network-lost', Lang.bind(this, function(emitter) {
this._ensureSource();
let icon = new St.Icon({ icon_name: 'network-offline',
icon_type: St.IconType.SYMBOLIC,
icon_size: this._source.ICON_SIZE
});
let notification = new MessageTray.Notification(this._source,
_("Connectivity lost"),
_("You're no longer connected to the network"),
{ icon: icon });
this._source.notify(notification);
}));
wrapper._activationFailedId = wrapper.connect('activation-failed', Lang.bind(this, function(wrapper, reason) {
this._ensureSource();
let icon = new St.Icon({ icon_name: 'network-error',
icon_type: St.IconType.SYMBOLIC,
icon_size: this._source.ICON_SIZE,
});
let banner;
wrapper._activationFailedId = wrapper.connect('activation-failed', Lang.bind(this, function(device, reason) {
// XXX: nm-applet has no special text depending on reason
// but I'm not sure of this generic message
let notification = new MessageTray.Notification(this._source,
_("Connection failed"),
_("Activation of network connection failed"),
{ icon: icon });
this._source.notify(notification);
})); */
this._notifyForDevice(device, 'network-error',
_("Connection failed"),
_("Activation of network connection failed"),
MessageTray.Urgency.HIGH);
}));
wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
this._syncSectionTitle(dev.category);
}));
wrapper._destroyId = wrapper.connect('destroy', function(wrapper) {
//wrapper.disconnect(wrapper._networkLostId);
//wrapper.disconnect(wrapper._activationFailedId);
wrapper.disconnect(wrapper._activationFailedId);
wrapper.disconnect(wrapper._deviceStateChangedId);
wrapper.disconnect(wrapper._destroyId);
});
let section = this._devices[wrapper.category].section;
let devices = this._devices[wrapper.category].devices;
@ -1798,11 +1809,8 @@ NMApplet.prototype = {
active._primaryDevice.setActiveConnection(null);
active._primaryDevice = null;
}
if (active._notifyStateId) {
active.disconnect(active._notifyStateId);
active._notifyStateId = 0;
}
if (active._inited) {
active.disconnect(active._notifyStateId);
active.disconnect(active._notifyDefaultId);
active.disconnect(active._notifyDefault6Id);
active._inited = false;
@ -1820,14 +1828,7 @@ NMApplet.prototype = {
if (!a._inited) {
a._notifyDefaultId = a.connect('notify::default', Lang.bind(this, this._updateIcon));
a._notifyDefault6Id = a.connect('notify::default6', Lang.bind(this, this._updateIcon));
if (a.state == NetworkManager.ActiveConnectionState.ACTIVATING) // prepare to notify to the user
a._notifyStateId = a.connect('notify::state', Lang.bind(this, this._notifyActiveConnection));
else {
// notify as soon as possible
Mainloop.idle_add(Lang.bind(this, function() {
this._notifyActiveConnection(a);
}));
}
a._notifyStateId = a.connect('notify::state', Lang.bind(this, this._notifyActivated));
a._inited = true;
}
@ -1871,67 +1872,26 @@ NMApplet.prototype = {
if (a._primaryDevice)
a._primaryDevice.setActiveConnection(a);
if (a.state == NetworkManager.ActiveConnectionState.ACTIVATED
&& a._primaryDevice && a._primaryDevice._notification) {
a._primaryDevice._notification.destroy();
a._primaryDevice._notification = null;
}
}
}
this._mainConnection = activating || default_ip4 || default_ip6 || this._activeConnections[0] || null;
},
_notifyActiveConnection: function(active) {
// FIXME: duplicate notifications when nm-applet is running
// This code will come back when nm-applet is killed
this._syncNMState();
return;
if (active.state == NetworkManager.ActiveConnectionState.ACTIVATED) {
// notify only connections that are visible
if (active._connection) {
this._ensureSource();
let icon;
let banner;
switch (active._section) {
case NMConnectionCategory.WWAN:
icon = 'network-cellular-signal-excellent';
banner = _("You're now connected to mobile broadband connection '%s'").format(active._connection._name);
break;
case NMConnectionCategory.WIRELESS:
icon = 'network-wireless-signal-excellent';
banner = _("You're now connected to wireless network '%s'").format(active._connection._name);
break;
case NMConnectionCategory.WIRED:
icon = 'network-wired';
banner = _("You're now connected to wired network '%s'").format(active._connection._name);
break;
case NMConnectionCategory.VPN:
icon = 'network-vpn';
banner = _("You're now connected to VPN network '%s'").format(active._connection._name);
break;
default:
// a fallback for a generic 'connected' icon
icon = 'network-transmit-receive';
banner = _("You're now connected to '%s'").format(active._connection._name);
}
let iconActor = new St.Icon({ icon_name: icon,
icon_type: St.IconType.SYMBOLIC,
icon_size: this._source.ICON_SIZE
});
let notification = new MessageTray.Notification(this._source,
_("Connection established"),
banner,
{ icon: iconActor });
this._source.notify(notification);
}
if (active._stateChangeId) {
active.disconnect(active._stateChangeId);
active._stateChangeId = 0;
}
_notifyActivated: function(activeConnection) {
if (activeConnection.state == NetworkManager.ActiveConnectionState.ACTIVATED
&& activeConnection._primaryDevice && activeConnection._primaryDevice._notification) {
activeConnection._primaryDevice._notification.destroy();
activeConnection._primaryDevice._notification = null;
}
this._syncNMState();
this._updateIcon();
},
_readConnections: function() {

View File

@ -75,16 +75,11 @@ 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._devicesChanged();
@ -96,7 +91,6 @@ Indicator.prototype = {
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;
@ -124,14 +118,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;
@ -144,7 +135,6 @@ Indicator.prototype = {
this._deviceItems = [];
if (error) {
this._deviceSep.actor.hide();
return;
}
@ -159,11 +149,6 @@ Indicator.prototype = {
this.menu.addMenuItem(item, this._otherDevicePosition + position);
position++;
}
if (this._hasPrimary && position > 0)
this._deviceSep.actor.show();
else
this._deviceSep.actor.hide();
}));
},
@ -206,7 +191,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 });
},

View File

@ -29,13 +29,12 @@ Indicator.prototype = {
PanelMenu.SystemStatusButton.prototype._init.call(this, 'audio-volume-muted', null);
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 +46,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 +59,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 +80,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 +88,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 +131,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 +153,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 +179,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 +201,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,8 +211,7 @@ 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,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);
}
};

View File

@ -1,6 +1,7 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const DBus = imports.dbus;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const Lang = imports.lang;
const Mainloop = imports.mainloop;
@ -11,6 +12,7 @@ const Tpl = imports.gi.TelepathyLogger;
const Tp = imports.gi.TelepathyGLib;
const History = imports.misc.history;
const Params = imports.misc.params;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray;
@ -43,11 +45,16 @@ let contactFeatures = [Tp.ContactFeature.ALIAS,
function makeMessageFromTpMessage(tpMessage, direction) {
let [text, flags] = tpMessage.to_text();
let timestamp = tpMessage.get_sent_timestamp();
if (timestamp == 0)
timestamp = tpMessage.get_received_timestamp();
return {
messageType: tpMessage.get_message_type(),
text: text,
sender: tpMessage.sender.alias,
timestamp: tpMessage.get_received_timestamp(),
timestamp: timestamp,
direction: direction
};
}
@ -76,12 +83,15 @@ Client.prototype = {
this._chatSources = {};
this._chatState = Tp.ChannelChatState.ACTIVE;
// account path -> AccountNotification
this._accountNotifications = {};
// Set up a SimpleObserver, which will call _observeChannels whenever a
// channel matching its filters is detected.
// The second argument, recover, means _observeChannels will be run
// for any existing channel as well.
let dbus = Tp.DBusDaemon.dup();
this._tpClient = new Shell.TpClient({ 'dbus_daemon': dbus,
this._accountManager = Tp.AccountManager.dup();
this._tpClient = new Shell.TpClient({ 'account-manager': this._accountManager,
'name': 'GnomeShell',
'uniquify-name': true })
this._tpClient.set_observe_channels_func(
@ -91,6 +101,11 @@ Client.prototype = {
this._tpClient.set_handle_channels_func(
Lang.bind(this, this._handleChannels));
// Workaround for gjs not supporting GPtrArray in signals.
// See BGO bug #653941 for context.
this._tpClient.set_contact_list_changed_func(
Lang.bind(this, this._contactListChanged));
// Allow other clients (such as Empathy) to pre-empt our channels if
// needed
this._tpClient.set_delegated_channels_callback(
@ -101,6 +116,22 @@ Client.prototype = {
} catch (e) {
throw new Error('Couldn\'t register Telepathy client. Error: \n' + e);
}
// Watch subscription requests and connection errors
this._subscriptionSource = null;
this._accountSource = null;
let factory = this._accountManager.get_factory();
factory.add_account_features([Tp.Account.get_feature_quark_connection()]);
factory.add_connection_features([Tp.Connection.get_feature_quark_contact_list()]);
factory.add_contact_features([Tp.ContactFeature.SUBSCRIPTION_STATES,
Tp.ContactFeature.ALIAS,
Tp.ContactFeature.AVATAR_DATA]);
this._accountManager.connect('account-validity-changed',
Lang.bind(this, this._accountValidityChanged));
this._accountManager.prepare_async(null, Lang.bind(this, this._accountManagerPrepared));
},
_observeChannels: function(observer, account, conn, channels,
@ -225,7 +256,8 @@ Client.prototype = {
// FIXME: We don't have a 'chat room' icon (bgo #653737) use
// system-users for now as Empathy does.
let source = new ApproverSource(dispatchOp, _("Invitation"), 'system-users');
let source = new ApproverSource(dispatchOp, _("Invitation"),
Shell.util_icon_from_string('system-users'));
Main.messageTray.add(source);
let notif = new RoomInviteNotification(source, dispatchOp, channel, contacts[0]);
@ -243,6 +275,8 @@ Client.prototype = {
else if (chanType == Tp.IFACE_CHANNEL_TYPE_STREAMED_MEDIA ||
chanType == 'org.freedesktop.Telepathy.Channel.Type.Call.DRAFT')
this._approveCall(account, conn, channel, dispatchOp, context);
else if (chanType == Tp.IFACE_CHANNEL_TYPE_FILE_TRANSFER)
this._approveFileTransfer(account, conn, channel, dispatchOp, context);
},
_approveTextChannel: function(account, conn, channel, dispatchOp, context) {
@ -288,7 +322,9 @@ Client.prototype = {
isVideo = true;
// We got the TpContact
let source = new ApproverSource(dispatchOp, _("Call"), isVideo ? 'camera-web' : 'audio-input-microphone');
let source = new ApproverSource(dispatchOp, _("Call"), isVideo ?
Shell.util_icon_from_string('camera-web') :
Shell.util_icon_from_string('audio-input-microphone'));
Main.messageTray.add(source);
let notif = new AudioVideoNotification(source, dispatchOp, channel, contacts[0], isVideo);
@ -296,9 +332,149 @@ Client.prototype = {
context.accept();
},
_approveFileTransfer: function(account, conn, channel, dispatchOp, context) {
let [targetHandle, targetHandleType] = channel.get_handle();
Shell.get_tp_contacts(conn, [targetHandle],
contactFeatures,
Lang.bind(this, this._createFileTransferSource, channel, context, dispatchOp));
},
_createFileTransferSource: function(connection, contacts, failed, channel, context, dispatchOp) {
if (contacts.length < 1) {
Shell.decline_dispatch_op(context, 'Failed to get file sender');
return;
}
// Use the icon of the file being transferred
let gicon = Gio.content_type_get_icon(channel.get_mime_type());
// We got the TpContact
let source = new ApproverSource(dispatchOp, _("File Transfer"), gicon);
Main.messageTray.add(source);
let notif = new FileTransferNotification(source, dispatchOp, channel, contacts[0]);
source.notify(notif);
context.accept();
},
_delegatedChannelsCb: function(client, channels) {
// Nothing to do as we don't make a distinction between observed and
// handled channels.
},
_accountManagerPrepared: function(am, result) {
am.prepare_finish(result);
let accounts = am.get_valid_accounts();
for (let i = 0; i < accounts.length; i++) {
this._accountValidityChanged(am, accounts[i], true);
}
},
_accountValidityChanged: function(am, account, valid) {
if (!valid)
return;
// It would be better to connect to "status-changed" but we cannot.
// See discussion in https://bugzilla.gnome.org/show_bug.cgi?id=654159
account.connect("notify::connection-status",
Lang.bind(this, this._accountConnectionStatusNotifyCb));
account.connect('notify::connection',
Lang.bind(this, this._connectionChanged));
this._connectionChanged(account);
},
_connectionChanged: function(account) {
let conn = account.get_connection();
if (conn == null)
return;
this._tpClient.grab_contact_list_changed(conn);
if (conn.get_contact_list_state() == Tp.ContactListState.SUCCESS) {
this._contactListChanged(conn, conn.dup_contact_list(), []);
}
},
_contactListChanged: function(conn, added, removed) {
for (let i = 0; i < added.length; i++) {
let contact = added[i];
contact.connect('subscription-states-changed',
Lang.bind(this, this._subscriptionStateChanged));
this._subscriptionStateChanged(contact);
}
},
_subscriptionStateChanged: function(contact) {
if (contact.get_publish_state() != Tp.SubscriptionState.ASK)
return;
/* Implicitly accept publish requests if contact is already subscribed */
if (contact.get_subscribe_state() == Tp.SubscriptionState.YES ||
contact.get_subscribe_state() == Tp.SubscriptionState.ASK) {
contact.authorize_publication_async(function(src, result) {
src.authorize_publication_finish(result)});
return;
}
/* Display notification to ask user to accept/reject request */
let source = this._ensureSubscriptionSource();
let notif = new SubscriptionRequestNotification(source, contact);
source.notify(notif);
},
_ensureSubscriptionSource: function() {
if (this._subscriptionSource == null) {
this._subscriptionSource = new MultiNotificationSource(
_("Subscription request"), 'gtk-dialog-question');
Main.messageTray.add(this._subscriptionSource);
this._subscriptionSource.connect('destroy', Lang.bind(this, function () {
this._subscriptionSource = null;
}));
}
return this._subscriptionSource;
},
_accountConnectionStatusNotifyCb: function(account) {
let connectionError = account.connection_error;
if (account.connection_status != Tp.ConnectionStatus.DISCONNECTED ||
connectionError == Tp.error_get_dbus_name(Tp.Error.CANCELLED)) {
return;
}
let notif = this._accountNotifications[account.get_object_path()];
if (notif)
return;
/* Display notification that account failed to connect */
let source = this._ensureAccountSource();
notif = new AccountNotification(source, account, connectionError);
this._accountNotifications[account.get_object_path()] = notif;
notif.connect('destroy', Lang.bind(this, function() {
delete this._accountNotifications[account.get_object_path()];
}));
source.notify(notif);
},
_ensureAccountSource: function() {
if (this._accountSource == null) {
this._accountSource = new MultiNotificationSource(
_("Connection error"), 'gtk-dialog-error');
Main.messageTray.add(this._accountSource);
this._accountSource.connect('destroy', Lang.bind(this, function () {
this._accountSource = null;
}));
}
return this._accountSource;
}
};
@ -326,6 +502,7 @@ ChatSource.prototype = {
this._notification = new ChatNotification(this);
this._notification.setUrgency(MessageTray.Urgency.HIGH);
this._notifyTimeoutId = 0;
// We ack messages when the message box is collapsed if user has
// interacted with it before and so read the messages:
@ -487,7 +664,22 @@ ChatSource.prototype = {
message = makeMessageFromTpMessage(message, NotificationDirection.RECEIVED);
this._notification.appendMessage(message);
this.notify();
// Wait a bit before notifying for the received message, a handler
// could ack it in the meantime.
if (this._notifyTimeoutId != 0)
Mainloop.source_remove(this._notifyTimeoutId);
this._notifyTimeoutId = Mainloop.timeout_add(500,
Lang.bind(this, this._notifyTimeout));
},
_notifyTimeout: function() {
if (this._pendingMessages.length != 0)
this.notify();
this._notifyTimeoutId = 0;
return false;
},
// This is called for both messages we send from
@ -576,13 +768,10 @@ ChatSource.prototype = {
},
_ackMessages: function() {
if (this._pendingMessages.length == 0)
return;
// Don't clear our messages here, tp-glib will send a
// 'pending-message-removed' for each one.
this._channel.ack_messages_async(this._pendingMessages, Lang.bind(this, function(src, result) {
this._channel.ack_messages_finish(result);}));
this._channel.ack_all_pending_messages_async(Lang.bind(this, function(src, result) {
this._channel.ack_all_pending_messages_finish(result);}));
},
_summaryItemClicked: function(source, button) {
@ -623,6 +812,8 @@ ChatNotification.prototype = {
this._oldMaxScrollAdjustment = 0;
this._createScrollArea();
this._lastGroup = null;
this._lastGroupActor = null;
this._scrollArea.vscroll.adjustment.connect('changed', Lang.bind(this, function(adjustment) {
let currentValue = adjustment.value + adjustment.page_size;
@ -650,12 +841,10 @@ ChatNotification.prototype = {
* @noTimestamp: Whether to add a timestamp. If %true, no timestamp
* will be added, regardless of the difference since the
* last timestamp
* @styles: A list of CSS class names.
*/
appendMessage: function(message, noTimestamp, styles) {
appendMessage: function(message, noTimestamp) {
let messageBody = GLib.markup_escape_text(message.text, -1);
styles = styles || [];
styles.push(message.direction);
let styles = [message.direction];
if (message.messageType == Tp.ChannelTextMessageType.ACTION) {
let senderAlias = GLib.markup_escape_text(message.sender, -1);
@ -668,7 +857,14 @@ ChatNotification.prototype = {
bannerMarkup: true });
}
this._append(messageBody, styles, message.timestamp, noTimestamp);
let group = (message.direction == NotificationDirection.RECEIVED ?
'received' : 'sent');
this._append({ body: messageBody,
group: group,
styles: styles,
timestamp: message.timestamp,
noTimestamp: noTimestamp });
},
_filterMessages: function() {
@ -694,24 +890,65 @@ ChatNotification.prototype = {
for (let i = 0; i < expired.length; i++)
expired[i].actor.destroy();
}
let groups = this._contentArea.get_children();
for (let i = 0; i < groups.length; i ++) {
let group = groups[i];
if (group.get_children().length == 0)
group.destroy();
}
},
_append: function(text, styles, timestamp, noTimestamp) {
/**
* _append:
* @props: An object with the properties:
* body: The text of the message.
* group: The group of the message, one of:
* 'received', 'sent', 'meta'.
* styles: Style class names for the message to have.
* timestamp: The timestamp of the message.
* noTimestamp: suppress timestamp signal?
* childProps: props to add the actor with.
*/
_append: function(props) {
let currentTime = (Date.now() / 1000);
if (!timestamp)
timestamp = currentTime;
props = Params.parse(props, { body: null,
group: null,
styles: [],
timestamp: currentTime,
noTimestamp: false,
childProps: null });
// Reset the old message timeout
if (this._timestampTimeoutId)
Mainloop.source_remove(this._timestampTimeoutId);
let body = this.addBody(text, true);
let highlighter = new MessageTray.URLHighlighter(props.body,
true, // line wrap?
true); // allow markup?
let body = highlighter.actor;
let styles = props.styles;
for (let i = 0; i < styles.length; i ++)
body.add_style_class_name(styles[i]);
this._history.unshift({ actor: body, time: timestamp, realMessage: true });
let group = props.group;
if (group != this._lastGroup) {
let style = 'chat-group-' + group;
this._lastGroup = group;
this._lastGroupActor = new St.BoxLayout({ style_class: style,
vertical: true });
this.addActor(this._lastGroupActor);
}
if (!noTimestamp) {
this._lastGroupActor.add(body, props.childProps);
let timestamp = props.timestamp;
this._history.unshift({ actor: body, time: timestamp,
realMessage: group != 'meta' });
if (!props.noTimestamp) {
if (timestamp < currentTime - SCROLLBACK_IMMEDIATE_TIME)
this.appendTimestamp();
else
@ -738,18 +975,18 @@ ChatNotification.prototype = {
If applicable, replace %X with a strftime format valid for your
locale, without seconds. */
// xgettext:no-c-format
format = _("Sent at %X on %A");
// FIXME: The next two are stolen from calendar.js with the comment to avoid
// a string-freeze break. They should be replaced with better strings
// with 'Sent at', appropriate context and appropriate translator comment.
format = _("Sent at <b>%X</b> on <b>%A</b>");
} else if (date.getYear() == now.getYear()) {
/* Translators: Shown on calendar heading when selected day occurs on current year */
format = C_("calendar heading", "%A, %B %d");
/* Translators: this is a time format in the style of "Wednesday, May 25",
shown when you get a chat message in the same year. */
// xgettext:no-c-format
format = _("Sent on <b>%A</b>, <b>%B %d</b>");
} else {
/* Translators: Shown on calendar heading when selected day occurs on different year */
format = C_("calendar heading", "%A, %B %d, %Y");
/* Translators: this is a time format in the style of "Wednesday, May 25, 2012",
shown when you get a chat message in a different year. */
// xgettext:no-c-format
format = _("Sent on <b>%A</b>, <b>%B %d</b>, %Y");
}
return date.toLocaleFormat(format);
@ -759,11 +996,13 @@ ChatNotification.prototype = {
let lastMessageTime = this._history[0].time;
let lastMessageDate = new Date(lastMessageTime * 1000);
let timeLabel = this.addBody(this._formatTimestamp(lastMessageDate), false, { expand: true, x_fill: false, x_align: St.Align.END });
timeLabel.add_style_class_name('chat-meta-message');
this._history.unshift({ actor: timeLabel, time: lastMessageTime, realMessage: false });
this._timestampTimeoutId = 0;
let timeLabel = this._append({ body: this._formatTimestamp(lastMessageDate),
group: 'meta',
styles: ['chat-meta-message'],
childProps: { expand: true, x_fill: false,
x_align: St.Align.END },
noTimestamp: true,
timestamp: lastMessageTime });
this._filterMessages();
@ -775,9 +1014,10 @@ ChatNotification.prototype = {
this.update(text, null, { customContent: true, titleMarkup: true });
else
this.update(this.source.title, null, { customContent: true });
let label = this.addBody(text, true);
label.add_style_class_name('chat-meta-message');
this._history.unshift({ actor: label, time: (Date.now() / 1000), realMessage: false});
let label = this._append({ body: text,
group: 'meta',
styles: ['chat-meta-message'] });
this._filterMessages();
},
@ -789,9 +1029,11 @@ ChatNotification.prototype = {
/* Translators: this is the other person changing their old IM name to their new
IM name. */
let message = '<i>' + _("%s is now known as %s").format(oldAlias, newAlias) + '</i>';
let label = this.addBody(message, true);
label.add_style_class_name('chat-meta-message');
this._history.unshift({ actor: label, time: (Date.now() / 1000), realMessage: false });
let label = this._append({ body: message,
group: 'meta',
styles: ['chat-meta-message'] });
this.update(newAlias, null, { customContent: true });
this._filterMessages();
@ -844,17 +1086,17 @@ ChatNotification.prototype = {
}
};
function ApproverSource(dispatchOp, text, icon) {
this._init(dispatchOp, text, icon);
function ApproverSource(dispatchOp, text, gicon) {
this._init(dispatchOp, text, gicon);
}
ApproverSource.prototype = {
__proto__: MessageTray.Source.prototype,
_init: function(dispatchOp, text, icon) {
_init: function(dispatchOp, text, gicon) {
MessageTray.Source.prototype._init.call(this, text);
this._icon = icon;
this._gicon = gicon;
this._setSummaryIcon(this.createNotificationIcon());
this._dispatchOp = dispatchOp;
@ -877,7 +1119,7 @@ ApproverSource.prototype = {
},
createNotificationIcon: function() {
return new St.Icon({ icon_name: this._icon,
return new St.Icon({ gicon: this._gicon,
icon_type: St.IconType.FULLCOLOR,
icon_size: this.ICON_SIZE });
}
@ -971,3 +1213,326 @@ AudioVideoNotification.prototype = {
}));
}
};
// File Transfer
function FileTransferNotification(source, dispatchOp, channel, contact) {
this._init(source, dispatchOp, channel, contact);
}
FileTransferNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
_init: function(source, dispatchOp, channel, contact) {
MessageTray.Notification.prototype._init.call(this,
source,
/* To translators: The first parameter is
* the contact's alias and the second one is the
* file name. The string will be something
* like: "Alice is sending you test.ogg"
*/
_("%s is sending you %s").format(contact.get_alias(),
channel.get_filename()),
null,
{ customContent: true });
this.setResident(true);
this.addButton('decline', _("Decline"));
this.addButton('accept', _("Accept"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'decline':
dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE,
'', function(src, result) {
src.leave_channels_finish(result)});
break;
case 'accept':
dispatchOp.handle_with_time_async('', global.get_current_time(),
function(src, result) {
src.handle_with_time_finish(result)});
break;
}
this.destroy();
}));
}
};
// A notification source that can embed multiple notifications
function MultiNotificationSource(title, icon) {
this._init(title, icon);
}
MultiNotificationSource.prototype = {
__proto__: MessageTray.Source.prototype,
_init: function(title, icon) {
MessageTray.Source.prototype._init.call(this, title);
this._icon = icon;
this._setSummaryIcon(this.createNotificationIcon());
this._nbNotifications = 0;
},
notify: function(notification) {
MessageTray.Source.prototype.notify.call(this, notification);
this._nbNotifications += 1;
// Display the source while there is at least one notification
notification.connect('destroy', Lang.bind(this, function () {
this._nbNotifications -= 1;
if (this._nbNotifications == 0)
this.destroy();
}));
},
createNotificationIcon: function() {
return new St.Icon({ gicon: Shell.util_icon_from_string(this._icon),
icon_type: St.IconType.FULLCOLOR,
icon_size: this.ICON_SIZE });
}
};
// Subscription request
function SubscriptionRequestNotification(source, contact) {
this._init(source, contact);
}
SubscriptionRequestNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
_init: function(source, contact) {
MessageTray.Notification.prototype._init.call(this, source,
/* To translators: The parameter is the contact's alias */
_("%s would like permission to see when you are online").format(contact.get_alias()),
null, { customContent: true });
this._contact = contact;
this._connection = contact.get_connection();
let layout = new St.BoxLayout({ vertical: false });
// Display avatar
let iconBox = new St.Bin({ style_class: 'avatar-box' });
iconBox._size = 48;
let textureCache = St.TextureCache.get_default();
let file = contact.get_avatar_file();
if (file) {
let uri = file.get_uri();
iconBox.child = textureCache.load_uri_async(uri, iconBox._size, iconBox._size);
}
else {
iconBox.child = new St.Icon({ icon_name: 'avatar-default',
icon_type: St.IconType.FULLCOLOR,
icon_size: iconBox._size });
}
layout.add(iconBox);
// subscription request message
let label = new St.Label({ style_class: 'subscription-message',
text: contact.get_publish_request() });
layout.add(label);
this.addActor(layout);
this.addButton('decline', _("Decline"));
this.addButton('accept', _("Accept"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'decline':
contact.remove_async(function(src, result) {
src.remove_finish(result)});
break;
case 'accept':
// Authorize the contact and request to see his status as well
contact.authorize_publication_async(function(src, result) {
src.authorize_publication_finish(result)});
contact.request_subscription_async('', function(src, result) {
src.request_subscription_finish(result)});
break;
}
// rely on _subscriptionStatesChangedCb to destroy the
// notification
}));
this._changedId = contact.connect('subscription-states-changed',
Lang.bind(this, this._subscriptionStatesChangedCb));
this._invalidatedId = this._connection.connect('invalidated',
Lang.bind(this, this.destroy));
},
destroy: function() {
if (this._changedId != 0) {
this._contact.disconnect(this._changedId);
this._changedId = 0;
}
if (this._invalidatedId != 0) {
this._connection.disconnect(this._invalidatedId);
this._invalidatedId = 0;
}
MessageTray.Notification.prototype.destroy.call(this);
},
_subscriptionStatesChangedCb: function(contact, subscribe, publish, msg) {
// Destroy the notification if the subscription request has been
// answered
if (publish != Tp.SubscriptionState.ASK)
this.destroy();
}
};
function AccountNotification(source, account, connectionError) {
this._init(source, account, connectionError);
}
// Messages from empathy/libempathy/empathy-utils.c
// create_errors_to_message_hash()
/* Translator note: these should be the same messages that are
* used in Empathy, so just copy and paste from there. */
let _connectionErrorMessages = {};
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.NETWORK_ERROR)]
= _("Network error");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.AUTHENTICATION_FAILED)]
= _("Authentication failed");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.ENCRYPTION_ERROR)]
= _("Encryption error");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_NOT_PROVIDED)]
= _("Certificate not provided");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_UNTRUSTED)]
= _("Certificate untrusted");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_EXPIRED)]
= _("Certificate expired");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_NOT_ACTIVATED)]
= _("Certificate not activated");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_HOSTNAME_MISMATCH)]
= _("Certificate hostname mismatch");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_FINGERPRINT_MISMATCH)]
= _("Certificate fingerprint mismatch");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_SELF_SIGNED)]
= _("Certificate self-signed");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CANCELLED)]
= _("Status is set to offline");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.ENCRYPTION_NOT_AVAILABLE)]
= _("Encryption is not available");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_INVALID)]
= _("Certificate is invalid");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CONNECTION_REFUSED)]
= _("Connection has been refused");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CONNECTION_FAILED)]
= _("Connection can't be established");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CONNECTION_LOST)]
= _("Connection has been lost");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.ALREADY_CONNECTED)]
= _("This resource is already connected to the server");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CONNECTION_REPLACED)]
= _("Connection has been replaced by a new connection using the same resource");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.REGISTRATION_EXISTS)]
= _("The account already exists on the server");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.SERVICE_BUSY)]
= _("Server is currently too busy to handle the connection");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_REVOKED)]
= _("Certificate has been revoked");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_INSECURE)]
= _("Certificate uses an insecure cipher algorithm or is cryptographically weak");
_connectionErrorMessages[Tp.error_get_dbus_name(Tp.Error.CERT_LIMIT_EXCEEDED)]
= _("The length of the server certificate, or the depth of the server certificate chain, exceed the limits imposed by the cryptography library");
AccountNotification.prototype = {
__proto__: MessageTray.Notification.prototype,
_init: function(source, account, connectionError) {
MessageTray.Notification.prototype._init.call(this, source,
/* translators: argument is the account name, like
* name@jabber.org for example. */
_("Connection to %s failed").format(account.get_display_name()),
null, { customContent: true });
this._label = new St.Label();
this.addActor(this._label);
this._updateMessage(connectionError);
this._account = account;
this.addButton('reconnect', _("Reconnect"));
this.addButton('edit', _("Edit account"));
this.connect('action-invoked', Lang.bind(this, function(self, action) {
switch (action) {
case 'reconnect':
// If it fails again, a new notification should pop up with the
// new error.
account.reconnect_async(null, null);
break;
case 'edit':
let cmd = '/usr/bin/empathy-accounts'
+ ' --select-account=%s'
.format(account.get_path_suffix());
let app_info = Gio.app_info_create_from_commandline(cmd, null, 0,
null);
app_info.launch([], null, null);
break;
}
this.destroy();
}));
this._enabledId = account.connect('notify::enabled',
Lang.bind(this, function() {
if (!account.is_enabled())
this.destroy();
}));
this._invalidatedId = account.connect('invalidated',
Lang.bind(this, this.destroy));
this._connectionStatusId = account.connect('notify::connection-status',
Lang.bind(this, function() {
let status = account.connection_status;
if (status == Tp.ConnectionStatus.CONNECTED) {
this.destroy();
} else if (status == Tp.ConnectionStatus.DISCONNECTED) {
this._updateMessage(account.connection_error);
}
}));
},
_updateMessage: function(connectionError) {
let message;
if (connectionError in _connectionErrorMessages) {
message = _connectionErrorMessages[connectionError];
} else {
message = _("Unknown reason");
}
this._label.set_text(message);
},
destroy: function() {
if (this._enabledId != 0) {
this._account.disconnect(this._enabledId);
this._enabledId = 0;
}
if (this._invalidatedId != 0) {
this._account.disconnect(this._invalidatedId);
this._invalidatedId = 0;
}
if (this._connectionStatusId != 0) {
this._account.disconnect(this._connectionStatusId);
this._connectionStatusId = 0;
}
MessageTray.Notification.prototype.destroy.call(this);
}
};

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

@ -0,0 +1,662 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const AccountsService = imports.gi.AccountsService;
const DBus = imports.dbus;
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.
function IMStatusItem(label, iconName) {
this._init(label, iconName);
}
IMStatusItem.prototype = {
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
_init: function(label, iconName) {
PopupMenu.PopupBaseMenuItem.prototype._init.call(this);
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);
}
};
function IMUserNameItem() {
this._init();
}
IMUserNameItem.prototype = {
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
_init: function() {
PopupMenu.PopupBaseMenuItem.prototype._init.call(this,
{ 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);
}
};
function IMStatusChooserItem() {
this._init();
}
IMStatusChooserItem.prototype = {
__proto__: PopupMenu.PopupBaseMenuItem.prototype,
_init: function() {
PopupMenu.PopupBaseMenuItem.prototype._init.call (this,
{ 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.getStatus(Lang.bind(this, this._sessionStatusChanged));
this._presence.connect('StatusChanged',
Lang.bind(this, this._sessionStatusChanged));
this._currentPresence = undefined;
this._previousPresence = undefined;
this._accountMgr = Tp.AccountManager.dup()
this._accountMgr.connect('most-available-presence-changed',
Lang.bind(this, this._IMStatusChanged));
this._accountMgr.prepare_async(null, Lang.bind(this,
function(mgr) {
let [presence, s, msg] = mgr.get_most_available_presence();
this._previousPresence = presence;
this._IMStatusChanged(mgr, presence, s, 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();
}));
},
// 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 + '");');
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';
}
},
_IMStatusChanged: function(accountMgr, presence, status, message) {
if (presence == this._currentPresence)
return;
this._currentPresence = presence;
if (presence == Tp.ConnectionPresenceType.AVAILABLE)
this._presence.setStatus(GnomeSession.PresenceStatus.AVAILABLE);
if (!this._expectedPresence || presence != this._expectedPresence)
this._previousPresence = presence;
else
this._expectedPresence = undefined;
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) {
if (sessionStatus == GnomeSession.PresenceStatus.AVAILABLE)
return this._previousPresence;
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)
return Tp.ConnectionPresenceType.EXTENDED_AWAY;
}
return this._currentPresence;
},
_sessionStatusChanged: function(sessionPresence, 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);
}
};
function UserMenuButton() {
this._init();
}
UserMenuButton.prototype = {
__proto__: PanelMenu.Button.prototype,
_init: function() {
PanelMenu.Button.prototype._init.call(this, 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._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._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._presence.connect('StatusChanged',
Lang.bind(this, this._updateSwitch));
this._presence.getStatus(Lang.bind(this, this._updateSwitch));
this._account_mgr.connect('most-available-presence-changed',
Lang.bind(this, this._updatePresenceIcon));
this._account_mgr.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();
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._userManager.connect('notify::is-loaded',
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._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(presence, 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('activate', 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._account_mgr.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.setStatus(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();
}
}
};

View File

@ -210,6 +210,11 @@ SearchTab.prototype = {
this._searchResults.createProviderMeta(provider);
},
removeSearchProvider: function(provider) {
this._searchSystem.unregisterProvider(provider);
this._searchResults.destroyProviderMeta(provider);
},
startSearch: function(event) {
global.stage.set_key_focus(this._text);
this._text.event(event, false);
@ -297,7 +302,7 @@ SearchTab.prototype = {
_doSearch: function () {
this._searchTimeoutId = 0;
let text = this._text.get_text().replace(/^\s+/g, '').replace(/\s+$/g, '');
this._searchResults.updateSearch(text);
this._searchResults.doSearch(text);
return false;
}
@ -563,6 +568,10 @@ ViewSelector.prototype = {
addSearchProvider: function(provider) {
this._searchTab.addSearchProvider(provider);
},
removeSearchProvider: function(provider) {
this._searchTab.removeSearchProvider(provider);
}
};
Signals.addSignalMethods(ViewSelector.prototype);

View File

@ -16,8 +16,7 @@ WindowAttentionHandler.prototype = {
this._tracker = Shell.WindowTracker.get_default();
this._tracker.connect('startup-sequence-changed', Lang.bind(this, this._onStartupSequenceChanged));
let display = global.screen.get_display();
display.connect('window-demands-attention', Lang.bind(this, this._onWindowDemandsAttention));
global.display.connect('window-demands-attention', Lang.bind(this, this._onWindowDemandsAttention));
},
_onStartupSequenceChanged : function(tracker) {

View File

@ -19,47 +19,47 @@ const UNDIM_TIME = 0.250;
var dimShader = undefined;
function getDimShader() {
if (dimShader === null)
return null;
if (!dimShader) {
let source = Shell.get_file_contents_utf8_sync(global.datadir + '/shaders/dim-window.glsl');
try {
let shader = new Clutter.Shader();
shader.set_fragment_source(source, -1);
shader.compile();
dimShader = shader;
} catch (e) {
log(e.message);
dimShader = null;
}
}
function getDimShaderSource() {
if (!dimShader)
dimShader = Shell.get_file_contents_utf8_sync(global.datadir + '/shaders/dim-window.glsl');
return dimShader;
}
function getTopInvisibleBorder(metaWindow) {
let outerRect = metaWindow.get_outer_rect();
let inputRect = metaWindow.get_input_rect();
return outerRect.y - inputRect.y;
}
function WindowDimmer(actor) {
this._init(actor);
}
WindowDimmer.prototype = {
_init: function(actor) {
this.effect = new Clutter.ShaderEffect({ shader_type: Clutter.ShaderType.FRAGMENT_SHADER });
this.effect.set_shader_source(getDimShaderSource());
this.actor = actor;
},
set dimFraction(fraction) {
this._dimFraction = fraction;
let shader = getDimShader();
if (!Meta.prefs_get_attach_modal_dialogs() || !shader) {
this.actor.set_shader(null);
if (!Meta.prefs_get_attach_modal_dialogs()) {
this.effect.enabled = false;
return;
}
if (fraction > 0.01) {
this.actor.set_shader(shader);
this.actor.set_shader_param_float('height', this.actor.get_height());
this.actor.set_shader_param_float('fraction', fraction);
} else
this.actor.set_shader(null);
Shell.shader_effect_set_double_uniform(this.effect, 'height', this.actor.get_height());
Shell.shader_effect_set_double_uniform(this.effect, 'fraction', fraction);
if (!this.effect.actor)
this.actor.add_effect(this.effect);
} else {
if (this.effect.actor)
this.actor.remove_effect(this.effect);
}
},
get dimFraction() {
@ -69,11 +69,11 @@ WindowDimmer.prototype = {
_dimFraction: 0.0
};
function getWindowDimmer(texture) {
if (!texture._windowDimmer)
texture._windowDimmer = new WindowDimmer(texture);
function getWindowDimmer(actor) {
if (!actor._windowDimmer)
actor._windowDimmer = new WindowDimmer(actor);
return texture._windowDimmer;
return actor._windowDimmer;
}
function WindowManager() {
@ -239,7 +239,7 @@ WindowManager.prototype = {
_hasAttachedDialogs: function(window, ignoreWindow) {
var count = 0;
window.foreach_transient(function(win) {
if (win != ignoreWindow && win.get_window_type() == Meta.WindowType.MODAL_DIALOG)
if (win != ignoreWindow && win.is_attached_dialog())
count++;
return false;
});
@ -247,7 +247,7 @@ WindowManager.prototype = {
},
_checkDimming: function(window, ignoreWindow) {
let shouldDim = Meta.prefs_get_attach_modal_dialogs() && this._hasAttachedDialogs(window, ignoreWindow);
let shouldDim = this._hasAttachedDialogs(window, ignoreWindow);
if (shouldDim && !window._dimmed) {
window._dimmed = true;
@ -268,30 +268,28 @@ WindowManager.prototype = {
let actor = window.get_compositor_private();
if (!actor)
return;
let texture = actor.get_texture();
if (animate)
Tweener.addTween(getWindowDimmer(texture),
Tweener.addTween(getWindowDimmer(actor),
{ dimFraction: 1.0,
time: DIM_TIME,
transition: 'linear'
});
else
getWindowDimmer(texture).dimFraction = 1.0;
getWindowDimmer(actor).dimFraction = 1.0;
},
_undimWindow: function(window, animate) {
let actor = window.get_compositor_private();
if (!actor)
return;
let texture = actor.get_texture();
if (animate)
Tweener.addTween(getWindowDimmer(texture),
Tweener.addTween(getWindowDimmer(actor),
{ dimFraction: 0.0,
time: UNDIM_TIME,
transition: 'linear'
});
else
getWindowDimmer(texture).dimFraction = 0.0;
getWindowDimmer(actor).dimFraction = 0.0;
},
_mapWindow : function(shellwm, actor) {
@ -309,9 +307,7 @@ WindowManager.prototype = {
actor._windowType = type;
}));
if (actor.meta_window.get_window_type() == Meta.WindowType.MODAL_DIALOG
&& Meta.prefs_get_attach_modal_dialogs()
&& actor.get_meta_window().get_transient_for()) {
if (actor.meta_window.is_attached_dialog()) {
this._checkDimming(actor.get_meta_window().get_transient_for());
if (this._shouldAnimate()) {
actor.set_scale(1.0, 0.0);
@ -373,7 +369,6 @@ WindowManager.prototype = {
_destroyWindow : function(shellwm, actor) {
let window = actor.meta_window;
let parent = window.get_transient_for();
if (actor._notifyWindowTypeSignalId) {
window.disconnect(actor._notifyWindowTypeSignalId);
actor._notifyWindowTypeSignalId = 0;
@ -383,12 +378,14 @@ WindowManager.prototype = {
return win != window;
});
}
while (window.get_window_type() == Meta.WindowType.MODAL_DIALOG
&& parent) {
if (window.is_attached_dialog()) {
let parent = window.get_transient_for();
this._checkDimming(parent, window);
if (!Meta.prefs_get_attach_modal_dialogs()
|| !this._shouldAnimate())
break;
if (!this._shouldAnimate()) {
shellwm.completed_destroy(actor);
return;
}
actor.set_scale(1.0, 1.0);
actor.show();
this._destroying.push(actor);
@ -529,22 +526,22 @@ WindowManager.prototype = {
shellwm.completed_switch_workspace();
},
_startAppSwitcher : function(shellwm, binding, window, backwards) {
_startAppSwitcher : function(shellwm, binding, mask, window, backwards) {
/* prevent a corner case where both popups show up at once */
if (this._workspaceSwitcherPopup != null)
this._workspaceSwitcherPopup.actor.hide();
let tabPopup = new AltTab.AltTabPopup();
if (!tabPopup.show(backwards, binding))
if (!tabPopup.show(backwards, binding, mask))
tabPopup.destroy();
},
_startA11ySwitcher : function(shellwm, binding, window, backwards) {
Main.ctrlAltTabManager.popup(backwards);
_startA11ySwitcher : function(shellwm, binding, mask, window, backwards) {
Main.ctrlAltTabManager.popup(backwards, mask);
},
_showWorkspaceSwitcher : function(shellwm, binding, window, backwards) {
_showWorkspaceSwitcher : function(shellwm, binding, mask, window, backwards) {
if (global.screen.n_workspaces == 1)
return;

View File

@ -94,22 +94,44 @@ function WindowClone(realWindow) {
WindowClone.prototype = {
_init : function(realWindow) {
this.actor = new Clutter.Clone({ source: realWindow.get_texture(),
reactive: true,
x: realWindow.x,
y: realWindow.y });
this.actor._delegate = this;
this.realWindow = realWindow;
this.metaWindow = realWindow.meta_window;
this.metaWindow._delegate = this;
this.origX = realWindow.x;
this.origY = realWindow.y;
let [borderX, borderY] = this._getInvisibleBorderPadding();
this._windowClone = new Clutter.Clone({ source: realWindow.get_texture(),
x: -borderX,
y: -borderY });
// We expect this.actor to be used for all interaction rather than
// this._windowClone; as the former is reactive and the latter
// is not, this just works for most cases. However, for DND all
// actors are picked, so DND operations would operate on the clone.
// To avoid this, we hide it from pick.
Shell.util_set_hidden_from_pick(this._windowClone, true);
this.origX = realWindow.x + borderX;
this.origY = realWindow.y + borderY;
let outerRect = realWindow.meta_window.get_outer_rect();
// The MetaShapedTexture that we clone has a size that includes
// the invisible border; this is inconvenient; rather than trying
// to compensate all over the place we insert a ClutterGroup into
// the hierarchy that is sized to only the visible portion.
this.actor = new Clutter.Group({ reactive: true,
x: this.origX,
y: this.origY,
width: outerRect.width,
height: outerRect.height });
this.actor.add_actor(this._windowClone);
this.actor._delegate = this;
this._stackAbove = null;
this._sizeChangedId = this.realWindow.connect('size-changed', Lang.bind(this, function() {
this.emit('size-changed');
}));
this._sizeChangedId = this.realWindow.connect('size-changed',
Lang.bind(this, this._onRealWindowSizeChanged));
this._realWindowDestroyId = this.realWindow.connect('destroy',
Lang.bind(this, this._disconnectRealWindowSignals));
@ -161,7 +183,7 @@ WindowClone.prototype = {
// will look funny.
if (!this._selected &&
this.metaWindow != global.screen.get_display().focus_window)
this.metaWindow != global.display.focus_window)
this._zoomEnd();
}
},
@ -176,6 +198,32 @@ WindowClone.prototype = {
this._realWindowDestroyId = 0;
},
_getInvisibleBorderPadding: function() {
// We need to adjust the position of the actor because of the
// consequences of invisible borders -- in reality, the texture
// has an extra set of "padding" around it that we need to trim
// down.
// The outer rect paradoxically is the smaller rectangle,
// containing the positions of the visible frame. The input
// rect contains everything, including the invisible border
// padding.
let outerRect = this.metaWindow.get_outer_rect();
let inputRect = this.metaWindow.get_input_rect();
let [borderX, borderY] = [outerRect.x - inputRect.x,
outerRect.y - inputRect.y];
return [borderX, borderY];
},
_onRealWindowSizeChanged: function() {
let [borderX, borderY] = this._getInvisibleBorderPadding();
let outerRect = this.metaWindow.get_outer_rect();
this.actor.set_size(outerRect.width, outerRect.height);
this._windowClone.set_position(-borderX, -borderY);
this.emit('size-changed');
},
_onDestroy: function() {
this._disconnectRealWindowSignals();
@ -1440,7 +1488,7 @@ Workspace.prototype = {
time);
return true;
} else if (source.shellWorkspaceLaunch) {
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace,
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace ? this.metaWorkspace.index() : -1,
timestamp: time });
return true;
}

View File

@ -167,7 +167,7 @@ WorkspaceThumbnail.prototype = {
return true;
}));
this._background = new Clutter.Clone({ source: global.background_actor });
this._background = Meta.BackgroundActor.new_for_screen(global.screen);
this._contents.add_actor(this._background);
let monitor = Main.layoutManager.primaryMonitor;
@ -452,7 +452,7 @@ WorkspaceThumbnail.prototype = {
time);
return true;
} else if (source.shellWorkspaceLaunch) {
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace,
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace ? this.metaWorkspace.index() : -1,
timestamp: time });
return true;
}

View File

@ -551,7 +551,6 @@ WorkspacesDisplay.prototype = {
Lang.bind(this, this._onScrollEvent));
this._monitorIndex = Main.layoutManager.primaryIndex;
this._monitor = Main.layoutManager.monitors[this._monitorIndex];
this._thumbnailsBox = new WorkspaceThumbnail.ThumbnailsBox();
controls.add_actor(this._thumbnailsBox.actor);
@ -568,8 +567,6 @@ WorkspacesDisplay.prototype = {
this._updateAlwaysZoom();
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._updateAlwaysZoom));
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._workspacesChanged));
Main.xdndHandler.connect('drag-begin', Lang.bind(this, function(){
this._alwaysZoomOut = true;
@ -582,6 +579,7 @@ WorkspacesDisplay.prototype = {
this._switchWorkspaceNotifyId = 0;
this._nWorkspacesChangedId = 0;
this._itemDragBeginId = 0;
this._itemDragCancelledId = 0;
this._itemDragEndId = 0;
@ -590,7 +588,7 @@ WorkspacesDisplay.prototype = {
this._windowDragEndId = 0;
},
show: function() {
show: function() {
this._zoomOut = this._alwaysZoomOut;
this._zoomFraction = this._alwaysZoomOut ? 1 : 0;
this._updateZoom();
@ -613,6 +611,9 @@ WorkspacesDisplay.prototype = {
global.screen.connect('restacked',
Lang.bind(this, this._onRestacked));
if (this._nWorkspacesChangedId == 0)
this._nWorkspacesChangedId = global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._workspacesChanged));
if (this._itemDragBeginId == 0)
this._itemDragBeginId = Main.overview.connect('item-drag-begin',
Lang.bind(this, this._dragBegin));

View File

@ -33,9 +33,11 @@ kn
lt
lv
mr
ms
nb
nl
nn
or
pa
pl
pt

View File

@ -1,15 +1,23 @@
data/gnome-shell.desktop.in.in
data/org.gnome.shell.gschema.xml.in
js/gdm/loginDialog.js
js/gdm/powerMenu.js
js/misc/util.js
js/ui/appDisplay.js
js/ui/appFavorites.js
js/ui/autorunManager.js
js/ui/calendar.js
js/ui/contactDisplay.js
js/ui/dash.js
js/ui/dateMenu.js
js/ui/docDisplay.js
js/ui/endSessionDialog.js
js/ui/extensionSystem.js
js/ui/keyboard.js
js/ui/lookingGlass.js
js/ui/messageTray.js
js/ui/networkAgent.js
js/ui/notificationDaemon.js
js/ui/overview.js
js/ui/panel.js
js/ui/placeDisplay.js
@ -17,7 +25,7 @@ js/ui/polkitAuthenticationAgent.js
js/ui/popupMenu.js
js/ui/runDialog.js
js/ui/searchDisplay.js
js/ui/statusMenu.js
js/ui/shellMountOperation.js
js/ui/status/accessibility.js
js/ui/status/bluetooth.js
js/ui/status/keyboard.js
@ -25,11 +33,10 @@ js/ui/status/network.js
js/ui/status/power.js
js/ui/status/volume.js
js/ui/telepathyClient.js
js/ui/userMenu.js
js/ui/viewSelector.js
js/ui/windowAttentionHandler.js
js/ui/workspacesView.js
src/gvc/gvc-mixer-control.c
src/gdmuser/gdm-user.c
src/main.c
src/shell-app.c
src/shell-app-system.c

1005
po/be.po

File diff suppressed because it is too large Load Diff

1088
po/bg.po

File diff suppressed because it is too large Load Diff

1095
po/da.po

File diff suppressed because it is too large Load Diff

991
po/de.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1086
po/es.po

File diff suppressed because it is too large Load Diff

1240
po/fa.po

File diff suppressed because it is too large Load Diff

1338
po/fi.po

File diff suppressed because it is too large Load Diff

1131
po/fr.po

File diff suppressed because it is too large Load Diff

1019
po/gl.po

File diff suppressed because it is too large Load Diff

1195
po/gu.po

File diff suppressed because it is too large Load Diff

332
po/he.po
View File

@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gnome-shell master\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-07-16 17:26+0300\n"
"PO-Revision-Date: 2011-07-16 17:27+0200\n"
"POT-Creation-Date: 2011-08-18 23:52+0300\n"
"PO-Revision-Date: 2011-08-18 23:55+0200\n"
"Last-Translator: Yaron Shahrabani <sh.yaron@gmail.com>\n"
"Language-Team: Hebrew <sh.yaron@gmail.com>\n"
"Language: he\n"
@ -204,27 +204,27 @@ msgid "Execution of '%s' failed:"
msgstr "ההרצה של '%s' נכשלה:"
#. Translators: Filter to display all applications
#: ../js/ui/appDisplay.js:252
#: ../js/ui/appDisplay.js:253
msgid "All"
msgstr "הכול"
#: ../js/ui/appDisplay.js:351
#: ../js/ui/appDisplay.js:315
msgid "APPLICATIONS"
msgstr "יישומים"
#: ../js/ui/appDisplay.js:377
#: ../js/ui/appDisplay.js:373
msgid "SETTINGS"
msgstr "הגדרות"
#: ../js/ui/appDisplay.js:650
#: ../js/ui/appDisplay.js:681
msgid "New Window"
msgstr "חלון חדש"
#: ../js/ui/appDisplay.js:653
#: ../js/ui/appDisplay.js:684
msgid "Remove from Favorites"
msgstr "הסרה מהמועדפים"
#: ../js/ui/appDisplay.js:654
#: ../js/ui/appDisplay.js:685
msgid "Add to Favorites"
msgstr "הוספה למועדפים"
@ -238,6 +238,15 @@ msgstr "%s נוסף למועדפים שלך."
msgid "%s has been removed from your favorites."
msgstr "%s הוסר מהמועדפים שלך."
#: ../js/ui/autorunManager.js:591
#, c-format
msgid "Open with %s"
msgstr "פתיחה באמצעות %s"
#: ../js/ui/autorunManager.js:617
msgid "Eject"
msgstr "שליפה"
#. Translators: Shown in calendar event list for all day events
#. * Keep it short, best if you can use less then 10 characters
#.
@ -352,94 +361,94 @@ msgid "S"
msgstr "ש׳"
#. Translators: Text to show if there are no events
#: ../js/ui/calendar.js:701
#: ../js/ui/calendar.js:678
msgid "Nothing Scheduled"
msgstr "היומן ריק"
#. Translators: Shown on calendar heading when selected day occurs on current year
#: ../js/ui/calendar.js:717 ../js/ui/telepathyClient.js:749
#: ../js/ui/calendar.js:694
msgctxt "calendar heading"
msgid "%A, %B %d"
msgstr "%A, ה־%e ב%B"
#. Translators: Shown on calendar heading when selected day occurs on different year
#: ../js/ui/calendar.js:720 ../js/ui/telepathyClient.js:752
#: ../js/ui/calendar.js:697
msgctxt "calendar heading"
msgid "%A, %B %d, %Y"
msgstr "%A, ה־%e ב%B, %Y"
#: ../js/ui/calendar.js:730
#: ../js/ui/calendar.js:707
msgid "Today"
msgstr "היום"
#: ../js/ui/calendar.js:734
#: ../js/ui/calendar.js:711
msgid "Tomorrow"
msgstr "מחר"
#: ../js/ui/calendar.js:743
#: ../js/ui/calendar.js:720
msgid "This week"
msgstr "השבוע"
#: ../js/ui/calendar.js:751
#: ../js/ui/calendar.js:728
msgid "Next week"
msgstr "בשבוע הבא"
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1122
#: ../js/ui/dash.js:172 ../js/ui/messageTray.js:1123
msgid "Remove"
msgstr "הסרה"
#: ../js/ui/dateMenu.js:89
#: ../js/ui/dateMenu.js:90
msgid "Date and Time Settings"
msgstr "הגדרות תאריך ושעה"
#: ../js/ui/dateMenu.js:109
#: ../js/ui/dateMenu.js:110
msgid "Open Calendar"
msgstr "פתיחת היומן"
#. Translators: This is the time format with date used
#. in 24-hour mode.
#: ../js/ui/dateMenu.js:162
#: ../js/ui/dateMenu.js:167
msgid "%a %b %e, %R:%S"
msgstr "%a %b %e, %R:%S"
#: ../js/ui/dateMenu.js:163
#: ../js/ui/dateMenu.js:168
msgid "%a %b %e, %R"
msgstr "%a %b %e, %R"
#. Translators: This is the time format without date used
#. in 24-hour mode.
#: ../js/ui/dateMenu.js:167
#: ../js/ui/dateMenu.js:172
msgid "%a %R:%S"
msgstr "%a %R:%S"
#: ../js/ui/dateMenu.js:168
#: ../js/ui/dateMenu.js:173
msgid "%a %R"
msgstr "%a %R"
#. Translators: This is a time format with date used
#. for AM/PM.
#: ../js/ui/dateMenu.js:175
#: ../js/ui/dateMenu.js:180
msgid "%a %b %e, %l:%M:%S %p"
msgstr "%a %b %e, %l:%M:%S %p"
#: ../js/ui/dateMenu.js:176
#: ../js/ui/dateMenu.js:181
msgid "%a %b %e, %l:%M %p"
msgstr "%a %b %e, %l:%M %p"
#. Translators: This is a time format without date used
#. for AM/PM.
#: ../js/ui/dateMenu.js:180
#: ../js/ui/dateMenu.js:185
msgid "%a %l:%M:%S %p"
msgstr "%a %l:%M:%S %p"
#: ../js/ui/dateMenu.js:181
#: ../js/ui/dateMenu.js:186
msgid "%a %l:%M %p"
msgstr "%a %l:%M %p"
#. Translators: This is the date format to use when the calendar popup is
#. * shown - it is shown just below the time in the shell (e.g. "Tue 9:29 AM").
#.
#: ../js/ui/dateMenu.js:192
#: ../js/ui/dateMenu.js:197
msgid "%A %B %e, %Y"
msgstr "%A ה־%e ב%B, %Y"
@ -514,44 +523,49 @@ msgstr "המערכת מופעלת מחדש"
msgid "Cancel"
msgstr "ביטול"
#: ../js/ui/lookingGlass.js:641
#: ../js/ui/lookingGlass.js:640
msgid "No extensions installed"
msgstr "לא מותקנות הרחבות"
#: ../js/ui/lookingGlass.js:678
#: ../js/ui/lookingGlass.js:686
msgid "Enabled"
msgstr "פעיל"
#. translators:
#. * The device has been disabled
#: ../js/ui/lookingGlass.js:680 ../src/gvc/gvc-mixer-control.c:1091
#: ../js/ui/lookingGlass.js:688 ../src/gvc/gvc-mixer-control.c:1091
msgid "Disabled"
msgstr "מנוטרל"
#: ../js/ui/lookingGlass.js:682
#: ../js/ui/lookingGlass.js:690
msgid "Error"
msgstr "שגיאה"
#: ../js/ui/lookingGlass.js:684
#: ../js/ui/lookingGlass.js:692
msgid "Out of date"
msgstr "לא בתוקף"
#: ../js/ui/lookingGlass.js:709
#: ../js/ui/lookingGlass.js:717
msgid "View Source"
msgstr "צפייה במקור"
#: ../js/ui/lookingGlass.js:715
#: ../js/ui/lookingGlass.js:723
msgid "Web Page"
msgstr "דף אינטרנט"
#: ../js/ui/messageTray.js:1115
#: ../js/ui/messageTray.js:1116
msgid "Open"
msgstr "פתיחה"
#: ../js/ui/messageTray.js:2286
#: ../js/ui/messageTray.js:2277
msgid "System Information"
msgstr "פרטי המערכת"
#: ../js/ui/notificationDaemon.js:427 ../js/ui/status/power.js:238
#: ../src/shell-app.c:340
msgid "Unknown"
msgstr "לא ידוע"
#: ../js/ui/overview.js:89
msgid "Undo"
msgstr "ביטול"
@ -571,18 +585,18 @@ msgid "Dash"
msgstr "חלונית"
#. TODO - _quit() doesn't really work on apps in state STARTING yet
#: ../js/ui/panel.js:532
#: ../js/ui/panel.js:531
#, c-format
msgid "Quit %s"
msgstr "יציאה מ־%s"
#. Translators: If there is no suitable word for "Activities"
#. in your language, you can use the word for "Overview".
#: ../js/ui/panel.js:568
#: ../js/ui/panel.js:567
msgid "Activities"
msgstr "פעילויות"
#: ../js/ui/panel.js:951
#: ../js/ui/panel.js:876
msgid "Top Bar"
msgstr "הסרגל העליון"
@ -648,43 +662,9 @@ msgstr "בחיפוש..."
msgid "No matching results."
msgstr "אין תוצאות תואמות."
#: ../js/ui/statusMenu.js:192 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:262
msgid "Power Off..."
msgstr "כיבוי..."
#: ../js/ui/statusMenu.js:194 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:261
msgid "Suspend"
msgstr "השהיה"
#: ../js/ui/statusMenu.js:217
msgid "Available"
msgstr "פנוי"
#: ../js/ui/statusMenu.js:222
msgid "Busy"
msgstr "עסוק"
#: ../js/ui/statusMenu.js:230
msgid "My Account"
msgstr "החשבון שלי"
#: ../js/ui/statusMenu.js:234
msgid "System Settings"
msgstr "הגדרות המערכת"
#: ../js/ui/statusMenu.js:242
msgid "Lock Screen"
msgstr "נעילת המסך"
#: ../js/ui/statusMenu.js:247
msgid "Switch User"
msgstr "החלפת משתמש"
#: ../js/ui/statusMenu.js:252
msgid "Log Out..."
msgstr "ניתוק..."
#: ../js/ui/shellMountOperation.js:285
msgid "Wrong password, please try again"
msgstr "ססמה שגויה, נא לנסות שוב"
#: ../js/ui/status/accessibility.js:60
msgid "Zoom"
@ -759,11 +739,11 @@ msgstr "מנוטרל חומרתית"
msgid "Connection"
msgstr "חיבור"
#: ../js/ui/status/bluetooth.js:226 ../js/ui/status/network.js:493
#: ../js/ui/status/bluetooth.js:226 ../js/ui/status/network.js:497
msgid "disconnecting..."
msgstr "בהליכי ניתוק..."
#: ../js/ui/status/bluetooth.js:239 ../js/ui/status/network.js:499
#: ../js/ui/status/bluetooth.js:239 ../js/ui/status/network.js:503
msgid "connecting..."
msgstr "בהתחברות..."
@ -814,7 +794,7 @@ msgstr "תמיד להעניק גישה"
msgid "Grant this time only"
msgstr "הענקת גישה הפעם בלבד"
#: ../js/ui/status/bluetooth.js:409 ../js/ui/telepathyClient.js:954
#: ../js/ui/status/bluetooth.js:409 ../js/ui/telepathyClient.js:985
msgid "Reject"
msgstr "סירוב"
@ -855,148 +835,186 @@ msgid "OK"
msgstr "אישור"
#: ../js/ui/status/keyboard.js:71
msgid "Show Keyboard Layout..."
msgstr "הצגת פריסת המקלדת..."
msgid "Show Keyboard Layout"
msgstr "הצגת פריסת המקלדת"
#: ../js/ui/status/keyboard.js:75
msgid "Localization Settings"
msgstr "הגדרות אזוריות"
msgid "Region and Language Settings"
msgstr "הגדרות אזור ושפה"
#: ../js/ui/status/network.js:123
#: ../js/ui/statusMenu.js:192 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:262
msgid "Power Off..."
msgstr "כיבוי..."
#: ../js/ui/statusMenu.js:194 ../js/ui/statusMenu.js:196
#: ../js/ui/statusMenu.js:261
msgid "Suspend"
msgstr "השהיה"
#: ../js/ui/statusMenu.js:217
msgid "Available"
msgstr "פנוי"
#: ../js/ui/statusMenu.js:222
msgid "Busy"
msgstr "עסוק"
#: ../js/ui/statusMenu.js:230
msgid "My Account"
msgstr "החשבון שלי"
#: ../js/ui/statusMenu.js:234
msgid "System Settings"
msgstr "הגדרות המערכת"
#: ../js/ui/statusMenu.js:242
msgid "Lock Screen"
msgstr "נעילת המסך"
#: ../js/ui/statusMenu.js:247
msgid "Switch User"
msgstr "החלפת משתמש"
#: ../js/ui/statusMenu.js:252
msgid "Log Out..."
msgstr "ניתוק..."
#: ../js/ui/status/network.js:108
msgid "<unknown>"
msgstr "<לא ידוע>"
#. Translators: this indicates that wireless or wwan is disabled by hardware killswitch
#: ../js/ui/status/network.js:292
#: ../js/ui/status/network.js:296
msgid "disabled"
msgstr "מנוטרל"
#. Translators: this is for network devices that are physically present but are not
#. under NetworkManager's control (and thus cannot be used in the menu)
#: ../js/ui/status/network.js:491
#: ../js/ui/status/network.js:495
msgid "unmanaged"
msgstr "לא מנוהל"
#. Translators: this is for network connections that require some kind of key or password
#: ../js/ui/status/network.js:502
#: ../js/ui/status/network.js:506
msgid "authentication required"
msgstr "נדרש אימות"
#. Translators: this is for devices that require some kind of firmware or kernel
#. module, which is missing
#: ../js/ui/status/network.js:512
#: ../js/ui/status/network.js:516
msgid "firmware missing"
msgstr "הקושחה חסרה"
#. Translators: this is for wired network devices that are physically disconnected
#: ../js/ui/status/network.js:519
#: ../js/ui/status/network.js:523
msgid "cable unplugged"
msgstr "הכבל מנותק"
#. Translators: this is for a network device that cannot be activated (for example it
#. is disabled by rfkill, or it has no coverage
#: ../js/ui/status/network.js:524
#: ../js/ui/status/network.js:528
msgid "unavailable"
msgstr "לא זמין"
#: ../js/ui/status/network.js:526
#: ../js/ui/status/network.js:530
msgid "connection failed"
msgstr "החיבור נכשל"
#: ../js/ui/status/network.js:582 ../js/ui/status/network.js:1507
#: ../js/ui/status/network.js:586 ../js/ui/status/network.js:1511
msgid "More..."
msgstr "עוד..."
#. TRANSLATORS: this is the indication that a connection for another logged in user is active,
#. and we cannot access its settings (including the name)
#: ../js/ui/status/network.js:618 ../js/ui/status/network.js:1444
#: ../js/ui/status/network.js:622 ../js/ui/status/network.js:1448
msgid "Connected (private)"
msgstr "מחובר (פרטי)"
#: ../js/ui/status/network.js:703
#: ../js/ui/status/network.js:707
msgid "Auto Ethernet"
msgstr "אתרנט אוטומטי"
#: ../js/ui/status/network.js:771
#: ../js/ui/status/network.js:775
msgid "Auto broadband"
msgstr "פס רחב אוטומטי"
#: ../js/ui/status/network.js:774
#: ../js/ui/status/network.js:778
msgid "Auto dial-up"
msgstr "חיוג אוטומטי"
#. TRANSLATORS: this the automatic wireless connection name (including the network name)
#: ../js/ui/status/network.js:898 ../js/ui/status/network.js:1456
#: ../js/ui/status/network.js:902 ../js/ui/status/network.js:1460
#, c-format
msgid "Auto %s"
msgstr "%s אוטומטי"
#: ../js/ui/status/network.js:900
#: ../js/ui/status/network.js:904
msgid "Auto bluetooth"
msgstr "Bluetooth אוטומטי"
#: ../js/ui/status/network.js:1458
#: ../js/ui/status/network.js:1462
msgid "Auto wireless"
msgstr "אלחוטי אוטומטי"
#: ../js/ui/status/network.js:1550
#: ../js/ui/status/network.js:1554
msgid "Enable networking"
msgstr "הפעלת תכונת הרשת"
#: ../js/ui/status/network.js:1562
#: ../js/ui/status/network.js:1566
msgid "Wired"
msgstr "קווי"
#: ../js/ui/status/network.js:1573
#: ../js/ui/status/network.js:1577
msgid "Wireless"
msgstr "אלחוטי"
#: ../js/ui/status/network.js:1583
#: ../js/ui/status/network.js:1587
msgid "Mobile broadband"
msgstr "פס־רחב נייד"
#: ../js/ui/status/network.js:1593
#: ../js/ui/status/network.js:1597
msgid "VPN Connections"
msgstr "חיבורי VPN"
#: ../js/ui/status/network.js:1605
#: ../js/ui/status/network.js:1609
msgid "Network Settings"
msgstr "הגדרות הרשת"
#: ../js/ui/status/network.js:1897
#: ../js/ui/status/network.js:1901
#, c-format
msgid "You're now connected to mobile broadband connection '%s'"
msgstr "כרגע ישנו חיבור בינך ובין רשת הפס הרחב הניידת '%s'"
#: ../js/ui/status/network.js:1901
#: ../js/ui/status/network.js:1905
#, c-format
msgid "You're now connected to wireless network '%s'"
msgstr "כרגע ישנו חיבור בינך ובין הרשת האלחוטית '%s'"
#: ../js/ui/status/network.js:1905
#: ../js/ui/status/network.js:1909
#, c-format
msgid "You're now connected to wired network '%s'"
msgstr "כרגע ישנו חיבור בינך ובין הרשת הקווית '%s'"
#: ../js/ui/status/network.js:1909
#: ../js/ui/status/network.js:1913
#, c-format
msgid "You're now connected to VPN network '%s'"
msgstr "כרגע ישנו חיבור בינך ובין רשת ה־VPN '%s'"
#: ../js/ui/status/network.js:1914
#: ../js/ui/status/network.js:1918
#, c-format
msgid "You're now connected to '%s'"
msgstr "כעת ישנו חיבור בינך ובין '%s'"
#: ../js/ui/status/network.js:1922
#: ../js/ui/status/network.js:1926
msgid "Connection established"
msgstr "ההתחברות הצליחה"
#: ../js/ui/status/network.js:2048
#: ../js/ui/status/network.js:2052
msgid "Networking is disabled"
msgstr "תכונת הרשת מנוטרלת"
#: ../js/ui/status/network.js:2173
#: ../js/ui/status/network.js:2177
msgid "Network Manager"
msgstr "מנהל הרשתות"
@ -1090,10 +1108,6 @@ msgstr "טבלת שליטה"
msgid "Computer"
msgstr "מחשב"
#: ../js/ui/status/power.js:238 ../src/shell-app-system.c:1088
msgid "Unknown"
msgstr "לא ידוע"
#: ../js/ui/status/volume.js:43
msgid "Volume"
msgstr "עצמה"
@ -1105,31 +1119,36 @@ msgstr "מיקרופון"
#. We got the TpContact
#. FIXME: We don't have a 'chat room' icon (bgo #653737) use
#. system-users for now as Empathy does.
#: ../js/ui/telepathyClient.js:222
#: ../js/ui/telepathyClient.js:229
msgid "Invitation"
msgstr "הזמנה"
#. We got the TpContact
#: ../js/ui/telepathyClient.js:285
#: ../js/ui/telepathyClient.js:295
msgid "Call"
msgstr "התקשרות"
#: ../js/ui/telepathyClient.js:541
#. We got the TpContact
#: ../js/ui/telepathyClient.js:323
msgid "File Transfer"
msgstr "העברת קבצים"
#: ../js/ui/telepathyClient.js:573
#, c-format
msgid "%s is online."
msgstr "%s התחבר/ה."
#: ../js/ui/telepathyClient.js:546
#: ../js/ui/telepathyClient.js:578
#, c-format
msgid "%s is offline."
msgstr "%s התנתק/ה."
#: ../js/ui/telepathyClient.js:549
#: ../js/ui/telepathyClient.js:581
#, c-format
msgid "%s is away."
msgstr "'%s' מרוחק/ת."
#: ../js/ui/telepathyClient.js:552
#: ../js/ui/telepathyClient.js:584
#, c-format
msgid "%s is busy."
msgstr "%s עסוק/ה."
@ -1137,21 +1156,35 @@ msgstr "%s עסוק/ה."
#. Translators: this is a time format string followed by a date.
#. If applicable, replace %X with a strftime format valid for your
#. locale, without seconds.
#: ../js/ui/telepathyClient.js:741
#: ../js/ui/telepathyClient.js:770
#, no-c-format
msgid "Sent at %X on %A"
msgstr "נשלח ב־%X בשעה %A"
msgid "Sent at <b>%X</b> on <b>%A</b>"
msgstr "נשלח ב־<b>%X</b> בשעה <b>%A</b>"
#. Translators: this is a time format in the style of "Wednesday, May 25",
#. shown when you get a chat message in the same year.
#: ../js/ui/telepathyClient.js:776
#, no-c-format
msgid "Sent on <b>%A</b>, <b>%B %d</b>"
msgstr "נשלח ב<b>%A</b>, <b>ה־%d ב%B</b>"
#. Translators: this is a time format in the style of "Wednesday, May 25, 2012",
#. shown when you get a chat message in a different year.
#: ../js/ui/telepathyClient.js:781
#, no-c-format
msgid "Sent on <b>%A</b>, <b>%B %d</b>, %Y"
msgstr "נשלח ב<b>%A</b>, <b>ה־%d ב%B</b>, %Y"
#. Translators: this is the other person changing their old IM name to their new
#. IM name.
#: ../js/ui/telepathyClient.js:791
#: ../js/ui/telepathyClient.js:822
#, c-format
msgid "%s is now known as %s"
msgstr "השם של %s הוחלף ל־%s"
#. translators: argument is a room name like
#. * room@jabber.org for example.
#: ../js/ui/telepathyClient.js:898
#: ../js/ui/telepathyClient.js:929
#, c-format
msgid "Invitation to %s"
msgstr "הזמנה ל־%s"
@ -1159,35 +1192,45 @@ msgstr "הזמנה ל־%s"
#. translators: first argument is the name of a contact and the second
#. * one the name of a room. "Alice is inviting you to join room@jabber.org
#. * for example.
#: ../js/ui/telepathyClient.js:906
#: ../js/ui/telepathyClient.js:937
#, c-format
msgid "%s is inviting you to join %s"
msgstr "הוזמנת על ידי %s להצטרף אל %s"
#: ../js/ui/telepathyClient.js:908
#: ../js/ui/telepathyClient.js:939 ../js/ui/telepathyClient.js:1028
msgid "Decline"
msgstr "דחייה"
#: ../js/ui/telepathyClient.js:909
#: ../js/ui/telepathyClient.js:940 ../js/ui/telepathyClient.js:1029
msgid "Accept"
msgstr "אישור"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/telepathyClient.js:942
#: ../js/ui/telepathyClient.js:973
#, c-format
msgid "Video call from %s"
msgstr "שיחת וידאו מאת %s"
#. translators: argument is a contact name like Alice for example.
#: ../js/ui/telepathyClient.js:945
#: ../js/ui/telepathyClient.js:976
#, c-format
msgid "Call from %s"
msgstr "שיחה מאת %s"
#: ../js/ui/telepathyClient.js:955
#: ../js/ui/telepathyClient.js:986
msgid "Answer"
msgstr "מענה"
#. To translators: The first parameter is
#. * the contact's alias and the second one is the
#. * file name. The string will be something
#. * like: "Alice is sending you test.ogg"
#.
#: ../js/ui/telepathyClient.js:1022
#, c-format
msgid "%s is sending you %s"
msgstr "%s שולח/ת אליך %s"
#. Translators: this is the text displayed
#. in the search entry when no search is
#. active; it should not exceed ~30
@ -1196,16 +1239,16 @@ msgstr "מענה"
msgid "Type to search..."
msgstr "יש להקליד כדי לחפש..."
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:257
#: ../js/ui/viewSelector.js:140 ../src/shell-util.c:261
msgid "Search"
msgstr "חיפוש"
#: ../js/ui/windowAttentionHandler.js:40
#: ../js/ui/windowAttentionHandler.js:39
#, c-format
msgid "%s has finished starting"
msgstr "%s סיים את תהליך ההתחלה"
#: ../js/ui/windowAttentionHandler.js:42
#: ../js/ui/windowAttentionHandler.js:41
#, c-format
msgid "'%s' is ready"
msgstr "'%s' מוכן"
@ -1238,7 +1281,7 @@ msgstr "צלילי מערכת"
msgid "Print version"
msgstr "Print version"
#: ../src/shell-app.c:464
#: ../src/shell-app.c:566
#, c-format
msgid "Failed to launch '%s'"
msgstr "אירע כשל בטעינת '%s'"
@ -1255,13 +1298,13 @@ msgstr "בררת מחדל"
msgid "Authentication dialog was dismissed by the user"
msgstr "המשתמש בחר להתעלם מתיבת דו־שיח האימות"
#: ../src/shell-util.c:96
#: ../src/shell-util.c:100
msgid "Home Folder"
msgstr "תיקיית הבית"
#. Translators: this is the same string as the one found in
#. * nautilus
#: ../src/shell-util.c:111
#: ../src/shell-util.c:115
msgid "File System"
msgstr "מערכת הקבצים"
@ -1270,11 +1313,18 @@ msgstr "מערכת הקבצים"
#. * example, "Trash: some-directory". It means that the
#. * directory called "some-directory" is in the trash.
#.
#: ../src/shell-util.c:307
#: ../src/shell-util.c:311
#, c-format
msgid "%1$s: %2$s"
msgstr "%1$s: %2$s"
#: ../src/shell-util.c:600
msgid "calendar:week_start:0"
msgstr "calendar:week_start:0"
#~ msgid "Localization Settings"
#~ msgstr "הגדרות אזוריות"
#~ msgid "Less than a minute ago"
#~ msgstr "לפני פחות מדקה"

1204
po/hi.po

File diff suppressed because it is too large Load Diff

1097
po/hu.po

File diff suppressed because it is too large Load Diff

1193
po/id.po

File diff suppressed because it is too large Load Diff

998
po/it.po

File diff suppressed because it is too large Load Diff

1208
po/ja.po

File diff suppressed because it is too large Load Diff

1052
po/ko.po

File diff suppressed because it is too large Load Diff

1303
po/lt.po

File diff suppressed because it is too large Load Diff

1021
po/lv.po

File diff suppressed because it is too large Load Diff

1348
po/ms.po Normal file

File diff suppressed because it is too large Load Diff

929
po/nb.po

File diff suppressed because it is too large Load Diff

1555
po/or.po Normal file

File diff suppressed because it is too large Load Diff

979
po/pa.po

File diff suppressed because it is too large Load Diff

1078
po/pl.po

File diff suppressed because it is too large Load Diff

1104
po/pt.po

File diff suppressed because it is too large Load Diff

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