Compare commits

..

1 Commits
3.2.0 ... osk

Author SHA1 Message Date
fff607e1a4 Updated on-screen-keyboard patches (squashed) 2011-07-26 13:34:57 -04:00
182 changed files with 24700 additions and 51820 deletions

View File

@ -1,7 +1,7 @@
# Point to our macro directory and pick up user flags from the environment # Point to our macro directory and pick up user flags from the environment
ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
SUBDIRS = data js src browser-plugin tests po man SUBDIRS = data js src tests po man
EXTRA_DIST = \ EXTRA_DIST = \
.project \ .project \

289
NEWS
View File

@ -1,292 +1,3 @@
3.2.0
=====
* Prevent the fallback on-screen keyboard from showing up while
GNOME Shell is running [Dan, #659865]
* Disable code to reposition windows around the on-screen keyboard;
it wasn't finished or working properly. [Dan; #659643]
* Fix interaction between on-screen keyboard and notifications
[Dan; #658603]
* Fix menu-sizing problems in right-to-left locales. [Florian; #659827]
* Update chat icons in the message tray when an avatar image changes
[Marina; #659768]
* Fix problem with empty notification bubbles being left [Marina; #659862]
* Fix problem with chat notifications bouncing when new messages come in.
[Marina; #659768]
* Fix bug that was causing SIP calls to automatically be accepted in some
circumstances [Guillaume; #660084]
* Fix string that should have been marked translatable [Frédéric]
* Fix a crash that could happen during CSS transitions [Florian; #659676]
* Build fixes [Colin, Florian]
Contributors:
Guillaume Desmottes, Florian Müllner, Frédéric Péters, Colin Walters,
Dan Winship, Marina Zhurakhinskaya
Translations:
Friedel Wolff [af], Nilamdyuti Goswami [as], Ihar Hrachyshka [be],
Ivaylo Valkov [bg], Gil Forcada [ca], Carles Ferrando [ca@valencia],
Petr Kovar [cz], Mario Blättermann [de], Kris Thomsen [dk],
Tiffany Antopolski, Kristjan Schmidt [eo], Daniel Mustieles [es],
Inaki Larranaga Murgoitio [eu], Tommi Vainikainen [fi], Bruno Brouard [fr],
Fran Dieguez [gl], Yaron Shahrabani [he], Gabor Kelemen [hu],
Andika Triwidada [id], Jiro Matsuzawa [ja], Changwoo Ryu [ko],
Rudolfs Mazurs [lv], Aurimas Černius [lt], Kjartan Maraas [nb],
A S Alam [pa], Piotr Drąg [pl], Duarte Loreto [pt], Djavan Fagundes,
Rodolfo Ribeiro Gomes, Gabriel F. Vilar [pt_BR], Yuri Myasoedov [ru],
Daniel Nylander [se], Martin Srebotnjak [sl], Michal Štrba [sv],
Krishnababu Krothapalli, Praveen Illa [te], Cheng-Chia Tseng [zh_KH, zh_TW]
3.1.92
======
* Login screen
- Add the ability to set a logo at the top of the user list [Ray; #658062]
- Add fingerprint reader support [Ray; #657823]
- Add a power button offering the choice of Suspend/Restart/Power off
[Ray; #657822]
- Remove the option to view the current keyboad layout [Matthias; #659164]
- Make Control-Alt-Tab work for full keyboard access [Ray; #659177]
* Frequently initiate a full garbage collection; Spidermonkey isn't very good
at tracking the amount of resources we have allocated so this hopefully will
improve memory usage without affecting performance too much [Colin; #659254]
* Stop adding a notification when the network connection is lost
[Colin; #658954]
* When disabling notifications; display a notification
"Your chat status will be set to busy" [Florian; #652718]
* Fix keynav in network dialogs [Florian; #659133]
* Improve calendar styling [Sean; #641135, #651299]
* Shrink padding around panel buttons for narrow screens [Dan; #651299]
* Allow enabling the onscreen keyboard through the accessibility menu
[Dan; #612662]
* Fix problem that was causing VPN secret dialogs to be delayed before showing
[Florian; #658484]
* Make custom-keybindings for the window switcher that don't use alt
work correctly [Florian; #645200]
* Fix duplicate application icons in the Activities Overview [Colin; #659351]
* Bug fixes for dimming windows with attached modal dialogs
[Jasper, Owen; #659302, 659634]
* Add build-time support for BROWSER_PLUGIN_DIR environment variable
[Vincent; #659123]
* Build fixes [Vincent; #659194]
* Code cleanups and test cases
[Adel, Dan, Florian, Jasper; #651299, #658092, #658939]
* Misc bug fixes
[Adel, Colin, Cosimo, Dan, Florian, Giovanni, Jasper, Ray, Xavier;
#651299, #652837, #657249, #658004, #658150, #658239, #658469, #658598,
#658605, #659050, #659159, #659210, #659270, #659370, #659633]
Contributors:
Giovanni Campagna, Cosimo Cecchi, Xavier Claessens, Matthias Clasen,
Rui Matos, Florian Müllner, Jasper St. Pierre, Owen Taylor,
Vincent Untz, Colin Walters, Sean Wilson, Dan Winship
Translations:
Ihar Hrachyshka [be], Alexander Shopov, Ivaylo Valkov [bg],
Mario Blättermann [de], Jorge González, Daniel Mustieles [es],
Arash Mousavi [fa], Ville-Pekka Vainio [fi], Fran Dieguez [gl],
Sweta Kothari [gu], Gabor Kelemen [hu], Jiro Matsuzawa [ja],
Luca Ferretti [it], Rudolfs Mazurs [lv], Kjartan Maraas [nb], A S Alam [pa],
Piotr Drąg [pl], Duarte Loreto [pt], Yuri Myasoedov [ru],
Daniel Nylander [se], Matej Urbančič [sl], Miroslav Nikolić [sr, sr@latin],
Michal Štrba [sv], Tirumurti Vasudevan [ta], Phương Lê Hoàng [vi],
Aron Xu [zh_CN], Chao-Hsiung Liao [zh_HK, zh_TW]
3.1.91.1
========
* Add a browser plugin - this plugin, tied to extensions.gnome.org,
allows users to download and install shell extensions, and enable,
disable, and uninstall extensions they already have installed.
[Jasper; #658070, #658612]
* Improve adding links to URLs in notifications [Dan; #636252]
* Remove "connection lost" notifications after reconnecting [Giovanni; #658049]
* Hide the onscreen keyboard when leaving a text entry [Dan; #658591]
* Fixes for translated strings [Florian; #639987, #644097, #645037]
* Bug fixes for network menu [Florian; #658492]
* Code cleanup [Dan; #646934]
* Build fixes [Javier, Rico]
* Misc bug fixes [Emmanuele, Florian, Jasper, Marina, Matthias, Ray;
#652837, #658423, #658503, #658525, #658562, #658624, #658640, #658983]
Conributors:
Emmanuele Bassi, Giovanni Campagna, Matthias Clasen, Javier Jardón,
Florian Muellner, Jasper St. Pierre, Ray Strode, Rico Tzschichholz,
Dan Winship, Marina Zhurakhinskaya
Translations:
Ihar Hrachyshka [be], Bruce Cowan [en_GB], Jorge González,
Daniel Mustieles [es], Timo Jyrinki [fi], Bruno Brouard, Luc Guillemin,
Claude Paroz, Luc Pionchon [fr], Fran Dieguez [gl], Rajesh Ranjan [hi],
Andika Triwidada [id], Luca Ferretti [it], Changwoo Ryu [ko],
Rudolfs Mazurs [lt], Kjartan Maraas [nb], Manoj Kumar Giri [or],
A S Alam [pa], Piotr Drąg [pl], Duarte Loreto [pt], Henrique P. Machado,
Gabriel F. Vilar [pt_BR], Daniel Nylander [se], Matej Urbančič [sl],
Tirumurti Vasudevan [ta], Yinghua Wang [zh_CN],
Chao-Hsiung Liao [zh_HK, zh_TW]
3.1.91
======
* Fix problem with applications vanishing from alt-Tab when
desktop files change. [Colin; #657990]
* Fix interaction of on-screen keyboard with run-dialog and
Looking Glass console [Dan; #657986]
* Add public API for adding and removing search providers
[Philippe; #657548, #658113]
* Allow changing IM status with scroll wheel [Florian; #657973]
* Limit volume slider to 100% [Bastien; #657607]
* Change "Do Not Disturb" to "Notifications" in user menu [Florian; #652718]
* Switch browser in default favorites to Epiphany [Colin; #650616]
* Misc bug fixes [Dan, Florian, Jasper, Marc-Antoine, Rui;
#649631, #655069, #656142, #657703, #657759, #658007, #658065, #658176]
Contributors:
Rui Matos, Florian Müllner, Philippe Normand, Marc-Antoine Perennou,
Jasper St. Pierre, Colin Walters, Dan Winship
Translations:
Ihar Hrachyshka [be], Mario Blättermann [de], Kris Thomsen [da],
Jorge González [es], Arash Mousavi [fa], Fran Dieguez [gl],
Takayuki Kusano [ja],Aurimas Černius [lt], Kjartan Maraas [nb], A S Alam [pa],
Stas Solovey [ru], Daniel Nylander [se], Tirumurti Vasudevan [ta],
Chao-Hsiung Liao [zh_HK, zh_TW]
3.1.90.1
========
* Fix typo that was breaking the "Login Screen" mode [Marc-Antoine]
* Fix build with new gobject-introspection [Dan]
* Use a better icon for removable devices [Cosimo; #657757]
* Add support for asynchronous search provides [Philippe, Jasper, Seif; #655220]
* Misc bug fixes [Alex, Guillaume, Jasper; #657657, #657696]
* Misc build fixes [Adel; #657697]
Contributors:
Cosimo Cecchi, Guillaume Desmottes, Adel Gadllah, Alexander Larsson, Seif Lotfy,
Philippe Normand, Marc-Antoine Perennou, Jasper St. Pierre, Dan Winship
Translations:
Jorge González, Daniel Mustieles [es], Stas Solovey [ru]
3.1.90
======
* Add an on-screen keyboard that uses Caribou as a backend
[Nohemi, Dan; #612662]
* Allow searching for people in the overview using libfolks
as the backend [Morten; #643018]
* Add a "Login Screen" mode to be used when GDM is running; this
mode has a stripped down user interface, and also contains the
code to display the user list and authentication. [Ray; #657082]
* Rework user menu to separate out "Do Not Disturb" from the IM
status and to visually match GNOME Contacts. [Florian; #652837]
* Implement displaying images such as cover-art in notifications
[Neha, Marina; #621009]
* Support default actions for notifications [Florian; #655818]
* Networking
- Stop using nm-applet for dialogs; do them as proper system modal
dialogs in the shell code. [Giovanni; #650244]
- Fix handling of hidden access points [Giovanni; #646454]
* Telepathy integration
- Support subscription requests [Guillaume, Xavier; #653941]
- Notify on account connection errors [Alban, Jasper, Xavier; #654159]
- Allow approving file transfers [Guillaume; #653940]
- Improve styling of messages [Jasper; #640271]
* Extension system [Jasper; #654770]
- Support live enabling and disabling of extensions
- Add the ability to install extensions from HTTP
- Enhance D-Bus interface for controlling extensions
- Collect errors separately for each extension
* Add Main.panel.addToStatusArea for extension convenience
[Giovanni, Jasper, Marc-Antoine; #653205]
* Port to the new gnome-menus API. Clean up and speed up
application information loading [Colin; #648149, #656546]
* Use the accountsservice library rather than cut-and-pasted GDM code
[Florian; #650893]
* Add a D-Bus interface to take a screenshot; this will avoid various race
conditions with the current gnome-screenshot approach [Adel; #652952]
* Show numeric indicators to distinguish duplicate keyboard names
[Giovanni; #650128]
* Add GNOME Documents to the favorites list [Adel; #657520]
* Update the clock immediately on resume from suspend [Colin; #656403]
* Remove animation support from StAdjustment [Ray; #657082]
* Support configuration of calendar applications via gsettings
[Tassilo; #651190]
* Don't fade in alt-Tab - wait a bit and show it instantly [Rui; #652346]
* Darken workspace background on all workspaces [Rui; #656433]
* Improve detection of the starting day of the week [Florian; #649078]
* Add StButtonAccessible [Alejandro]
* Visual tweaks to match mockups
[Allan, Dan, Jasper, Marina; #640271, #655627, #655428, #656732]
* Misc bug fixes [Dan, Florian, Giovanni, Guillaume, Jasper, Jeremy, Rui;
#645708, #646761, #653119, #654398, #656125, #654707, #654898, #654638,
#656335, #657111]
* Code cleanups [Colin, Dan, Guillaume, Ray;
#652718, #654639, #648651, #655813, #657082]
* String tweaks [Jasper, Jeremy; #652984, #640271]
* Build fixes [Jasper, Nohemi; #644275, #655812]
Contributors:
Jeremy Bicha, Giovanni Campagna, Xavier Claessens, Alban Crequy,
Guillaume Desmottes, Allan Day, Neha Doijode, Nohemi Fernandez,
Tassilo Horn, Rui Matos, Morten Mjelva, Florian Müllner, Alejandro Piñeiro,
Jasper St. Pierre, Ray Strode, Colin Walters, Dan Winship,
Marina Zhurakhinskaya
Translations:
Ivaylo Valkov [bg], Mario Blättermann [de], Diego Escalante Urrelo,
Jorge González, Daniel Mustieles [es], Arash Mousavi [fa], Fran Dieguez [gl],
Yaron Shahrabani [he], Andika Triwidada, Wibiharto [id],
Aurimas Černius [lt], Umarzuki Bin Mochlis Moktar [ml], Kjartan Maraas [nb],
A S Alam [pa], Daniel Nylander [se], Ngô Chin, Nguyễn Thái Ngọc Duy [vi],
Aron Xu [zh_CN], Chao-Hsiung Liao [zh_HK, zh_TW]
3.1.4
=====
* Take over inserted media handling and autorun from gnome-session [Cosimo]
* Message Tray
- Display a count of unread notifications on icons
[Jasper, Guillaume; #649356, #654139]
- Only remove icons when the sender quits from D-Bus, not when it
closes its last window [Neha, Marina; #645764]
- Solve problems switching chats between shell and Empathy
[Guillaume; #654237]
- Fix handling of bad GMarkup in messages [Dan; #650298]
- Never show notifications when the screensaver is active [Dan; #654550]
* Telepathy integrationpp
- Implement Telepathy Debug interface to enable empathy-debugger
[Guillaume; #652816]
- Allow approving room invitations, and audio/video calls
[Guillaume; #653740 #653939]
- Send typing notifications [Jonny; #650196]
* Fix selection highlighting for light-on-dark entries [Jasper; #643768]
* Make control-Return in the overview open a new window [Maxim]
* Delay showing the alt-Tab switcher to reduce visual noise when
flipping betweeen windows [Dan; #652346]
* When we have vertically stacked monitors, put the message tray
on the bottom one [Dan; #636963]
* Fix various problems with keynav and the Activities button
[Dan; #641253 #645759]
* Ensure screensaver is locked when switching users [Colin; #654565]
* Improve extension creation tool [Jasper; #653206]
* Fix compatibility with latest GJS [Giovanni; #654349]
* Code cleanups [Adel, Dan, Jasper; #645759 #654577 #654791 #654987]
* Misc bug fixes [Richard, Dan, Florian, Giovanni, Jasper, Marc-Antoine, Rui;
#647175 #649513 #650452 #651082 #653700 #653989 #654105 #654791 #654267
#654269 #654527 #655446]
* Build fixes [Florian, Siegfried; #654300]
Contributors:
Giovanni Campagna, Cosimo Cecchi, Guillaume Desmottes, Neha Doijode,
Maxim Ermilov, Adel Gadllah, Siegfried-Angel Gevatter Pujals, Richard Hughes,
Jonny Lamb, Rui Matos, Florian Müllner, Marc-Antoine Perennou, Colin Walters,
Dan Winship, Marina Zhurakhinskaya
Translations:
Mario Blättermann, Paul Seyfert [de], Jorge González, Daniel Mustieles [es],
Fran Dieguez [gl], Yaron Shahrabani [he], Luca Ferretti [it],
Rudolfs Mazurs [lv], Kjartan Maraas [nb], A S Alam [pa], Yuri Kozlov [ru],
Michal Štrba, Matej Urbančič [sl]
3.1.3 3.1.3
===== =====
* Fix problem with "user theme extension" breaking the CSS for other * Fix problem with "user theme extension" breaking the CSS for other

View File

@ -1,21 +0,0 @@
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\"

View File

@ -1,17 +0,0 @@
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

@ -1,824 +0,0 @@
/* -*- 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

@ -1,893 +0,0 @@
/* -*- 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

@ -1,322 +0,0 @@
/* -*- 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

@ -1,393 +0,0 @@
/* -*- 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

@ -1,121 +0,0 @@
/* -*- 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_PREREQ(2.63)
AC_INIT([gnome-shell],[3.2.0],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell]) AC_INIT([gnome-shell],[3.1.3],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
AC_CONFIG_SRCDIR([src/shell-global.c]) 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_NAME], ["$PACKAGE_NAME"])
AC_SUBST([PACKAGE_VERSION], ["$PACKAGE_VERSION"]) AC_SUBST([PACKAGE_VERSION], ["$PACKAGE_VERSION"])
AM_INIT_AUTOMAKE([1.11 no-dist-gzip dist-xz tar-ustar foreign]) AM_INIT_AUTOMAKE([1.10 dist-bzip2 no-dist-gzip foreign])
AM_MAINTAINER_MODE([enable]) AM_MAINTAINER_MODE
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
@ -66,15 +66,14 @@ AM_CONDITIONAL(BUILD_RECORDER, $build_recorder)
CLUTTER_MIN_VERSION=1.7.5 CLUTTER_MIN_VERSION=1.7.5
GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1 GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1
GJS_MIN_VERSION=1.29.18 GJS_MIN_VERSION=0.7.11
MUTTER_MIN_VERSION=3.0.0 MUTTER_MIN_VERSION=3.0.0
FOLKS_MIN_VERSION=0.5.2
GTK_MIN_VERSION=3.0.0 GTK_MIN_VERSION=3.0.0
GIO_MIN_VERSION=2.29.10 GIO_MIN_VERSION=2.25.9
LIBECAL_MIN_VERSION=2.32.0 LIBECAL_MIN_VERSION=2.32.0
LIBEDATASERVER_MIN_VERSION=1.2.0 LIBEDATASERVER_MIN_VERSION=1.2.0
LIBEDATASERVERUI_MIN_VERSION=2.91.6 LIBEDATASERVERUI_MIN_VERSION=2.91.6
TELEPATHY_GLIB_MIN_VERSION=0.15.5 TELEPATHY_GLIB_MIN_VERSION=0.15.3
TELEPATHY_LOGGER_MIN_VERSION=0.2.4 TELEPATHY_LOGGER_MIN_VERSION=0.2.4
POLKIT_MIN_VERSION=0.100 POLKIT_MIN_VERSION=0.100
STARTUP_NOTIFICATION_MIN_VERSION=0.11 STARTUP_NOTIFICATION_MIN_VERSION=0.11
@ -83,10 +82,9 @@ STARTUP_NOTIFICATION_MIN_VERSION=0.11
PKG_CHECK_MODULES(GNOME_SHELL, gio-2.0 >= $GIO_MIN_VERSION PKG_CHECK_MODULES(GNOME_SHELL, gio-2.0 >= $GIO_MIN_VERSION
gio-unix-2.0 dbus-glib-1 libxml-2.0 gio-unix-2.0 dbus-glib-1 libxml-2.0
gtk+-3.0 >= $GTK_MIN_VERSION gtk+-3.0 >= $GTK_MIN_VERSION
folks >= $FOLKS_MIN_VERSION
libmutter >= $MUTTER_MIN_VERSION libmutter >= $MUTTER_MIN_VERSION
gjs-internals-1.0 >= $GJS_MIN_VERSION gjs-internals-1.0 >= $GJS_MIN_VERSION
libgnome-menu-3.0 $recorder_modules gconf-2.0 libgnome-menu $recorder_modules gconf-2.0
gdk-x11-3.0 libsoup-2.4 gdk-x11-3.0 libsoup-2.4
clutter-x11-1.0 >= $CLUTTER_MIN_VERSION clutter-x11-1.0 >= $CLUTTER_MIN_VERSION
clutter-glx-1.0 >= $CLUTTER_MIN_VERSION clutter-glx-1.0 >= $CLUTTER_MIN_VERSION
@ -95,21 +93,24 @@ PKG_CHECK_MODULES(GNOME_SHELL, gio-2.0 >= $GIO_MIN_VERSION
libcanberra libcanberra
telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
telepathy-logger-0.2 >= $TELEPATHY_LOGGER_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_PERF_HELPER, gtk+-3.0 gio-2.0)
PKG_CHECK_MODULES(SHELL_HOTPLUG_SNIFFER, gio-2.0 gdk-pixbuf-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` 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_DEFINE_UNQUOTED([GJS_VERSION], ["$GJS_VERSION"], [The version of GJS we're linking to])
AC_SUBST([GJS_VERSION], ["$GJS_VERSION"]) AC_SUBST([GJS_VERSION], ["$GJS_VERSION"])
GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION]) GOBJECT_INTROSPECTION_CHECK([$GOBJECT_INTROSPECTION_MIN_VERSION])
JHBUILD_TYPELIBDIR="$INTROSPECTION_TYPELIBDIR" 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) AC_SUBST(JHBUILD_TYPELIBDIR)
saved_CFLAGS=$CFLAGS saved_CFLAGS=$CFLAGS
@ -155,17 +156,6 @@ AC_CHECK_FUNCS(fdwalk)
AC_CHECK_FUNCS(mallinfo) AC_CHECK_FUNCS(mallinfo)
AC_CHECK_HEADERS([sys/resource.h]) 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 # Sets GLIB_GENMARSHAL and GLIB_MKENUMS
AM_PATH_GLIB_2_0() AM_PATH_GLIB_2_0()
G_IR_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0` G_IR_SCANNER=`$PKG_CONFIG --variable=g_ir_scanner gobject-introspection-1.0`
@ -210,41 +200,12 @@ AC_ARG_ENABLE(jhbuild-wrapper-script,
AS_HELP_STRING([--jhbuild-wrapper-script=yes],[Make "gnome-shell" script work for jhbuild]),,enable_jhbuild_wrapper_script=no) AS_HELP_STRING([--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) 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([ AC_CONFIG_FILES([
Makefile Makefile
data/Makefile data/Makefile
js/Makefile js/Makefile
js/misc/config.js js/misc/config.js
src/Makefile src/Makefile
browser-plugin/Makefile
tests/Makefile tests/Makefile
po/Makefile.in po/Makefile.in
man/Makefile man/Makefile

View File

@ -29,7 +29,6 @@ dist_theme_DATA = \
theme/dash-placeholder.svg \ theme/dash-placeholder.svg \
theme/filter-selected-ltr.svg \ theme/filter-selected-ltr.svg \
theme/filter-selected-rtl.svg \ theme/filter-selected-rtl.svg \
theme/gdm.css \
theme/gnome-shell.css \ theme/gnome-shell.css \
theme/panel-border.svg \ theme/panel-border.svg \
theme/panel-button-border.svg \ theme/panel-button-border.svg \

View File

@ -11,6 +11,15 @@
using the Alt-F2 dialog. using the Alt-F2 dialog.
</_description> </_description>
</key> </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"> <key name="enabled-extensions" type="as">
<default>[]</default> <default>[]</default>
<_summary>Uuids of extensions to enable</_summary> <_summary>Uuids of extensions to enable</_summary>
@ -31,7 +40,7 @@
</_description> </_description>
</key> </key>
<key name="favorite-apps" type="as"> <key name="favorite-apps" type="as">
<default>[ 'epiphany.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'libreoffice-writer.desktop', 'nautilus.desktop', 'gnome-documents.desktop' ]</default> <default>[ 'mozilla-firefox.desktop', 'evolution.desktop', 'empathy.desktop', 'rhythmbox.desktop', 'shotwell.desktop', 'openoffice.org-writer.desktop', 'nautilus.desktop' ]</default>
<_summary>List of desktop file IDs for favorite applications</_summary> <_summary>List of desktop file IDs for favorite applications</_summary>
<_description> <_description>
The applications corresponding to these identifiers The applications corresponding to these identifiers
@ -69,6 +78,13 @@
<schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/" <schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/"
gettext-domain="@GETTEXT_PACKAGE@"> gettext-domain="@GETTEXT_PACKAGE@">
<key name="show-keyboard" type="b">
<default>false</default>
<_summary>Show the onscreen keyboard</_summary>
<_description>
If true, display onscreen keyboard.
</_description>
</key>
<key name="keyboard-type" type="s"> <key name="keyboard-type" type="s">
<default>'touch'</default> <default>'touch'</default>
<_summary>Which keyboard to use</_summary> <_summary>Which keyboard to use</_summary>
@ -76,6 +92,20 @@
The type of keyboard to use. The type of keyboard to use.
</_description> </_description>
</key> </key>
<key name="enable-drag" type="b">
<default>false</default>
<_summary>Enable keyboard dragging</_summary>
<_description>
If true, enable keyboard dragging.
</_description>
</key>
<key name="enable-float" type="b">
<default>false</default>
<_summary>Enable floating keyboard</_summary>
<_description>
If true, enable floating keyboard.
</_description>
</key>
</schema> </schema>
<schema id="org.gnome.shell.clock" path="/org/gnome/shell/clock/" <schema id="org.gnome.shell.clock" path="/org/gnome/shell/clock/"

View File

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

View File

@ -1,166 +0,0 @@
/* 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 */ /* PopupMenu */
.popup-menu-boxpointer { .popup-menu-boxpointer {
-arrow-border-radius: 8px; -arrow-border-radius: 9px;
-arrow-background-color: rgba(0,0,0,0.9); -arrow-background-color: rgba(0,0,0,0.9);
-arrow-border-width: 2px; -arrow-border-width: 2px;
-arrow-border-color: #a5a5a5; -arrow-border-color: #5f5f5f;
-arrow-base: 24px; -arrow-base: 30px;
-arrow-rise: 11px; -arrow-rise: 15px;
} }
.popup-menu { .popup-menu {
@ -139,15 +139,6 @@ StTooltip StLabel {
border-width: 0px; 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 /* 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. * override .popup-menu.font-size, everything else will scale with it.
*/ */
@ -164,17 +155,9 @@ StTooltip StLabel {
background-color: #4c4c4c; background-color: #4c4c4c;
} }
.popup-menu-item:insensitive {
color: #9f9f9f;
}
.popup-image-menu-item { .popup-image-menu-item {
} }
.popup-combobox-item {
spacing: 1em;
}
.popup-separator-menu-item { .popup-separator-menu-item {
-gradient-height: 2px; -gradient-height: 2px;
-gradient-start: rgba(8,8,8,0); -gradient-start: rgba(8,8,8,0);
@ -240,39 +223,6 @@ StTooltip StLabel {
spacing: .5em; 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 */
#panel { #panel {
@ -280,12 +230,12 @@ StTooltip StLabel {
background-color: black; background-color: black;
border-image: url("panel-border.svg") 1; border-image: url("panel-border.svg") 1;
font-size: 10.5pt; font-size: 10.5pt;
font-weight: bold;
height: 1.86em; height: 1.86em;
} }
#panelLeft, #panelCenter { #panelLeft, #panelCenter, #panelRight {
spacing: 4px; spacing: 4px;
font-weight: bold;
} }
#panelLeft:ltr { #panelLeft:ltr {
@ -339,8 +289,7 @@ StTooltip StLabel {
} }
.panel-button { .panel-button {
-natural-hpadding: 12px; padding: 0px 12px;
-minimum-hpadding: 6px;
font-weight: bold; font-weight: bold;
color: #ccc; color: #ccc;
transition-duration: 100; transition-duration: 100;
@ -360,9 +309,9 @@ StTooltip StLabel {
text-shadow: black 0px 2px 2px; text-shadow: black 0px 2px 2px;
} }
.panel-status-button:active, #statusTray > .panel-button:active,
.panel-status-button:checked, #statusTray > .panel-button:checked,
.panel-status-button:focus { #statusTray > .panel-button:focus {
background-image: url("panel-button-highlight-narrow.svg"); background-image: url("panel-button-highlight-narrow.svg");
} }
@ -372,56 +321,34 @@ StTooltip StLabel {
icon-shadow: black 0px 2px 2px; icon-shadow: black 0px 2px 2px;
} }
.panel-menu { /* The rounded panel corners we draw don't
-boxpointer-gap: 4px * support transitions, so disable transitions
* for the buttons at the left/right edges
*/
#panelActivities {
transition-duration: 0;
} }
#panelUserMenu { #panelStatus {
transition-duration: 0;
}
#panelStatusMenu {
spacing: 4px; spacing: 4px;
} }
.status-chooser { #legacyTray {
spacing: .4em; spacing: 14px;
padding-left: 14px;
} }
.status-chooser .popup-menu-item, #legacyTray:rtl {
.status-chooser-combo .popup-menu-item { padding-left: 0px;
padding: .4em; padding-right: 14px;
} }
.status-chooser-user-icon { #legacyTray:compact {
border: 2px solid #8b8b8b; spacing: 8px;
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 { .system-status-icon {
@ -432,6 +359,7 @@ StTooltip StLabel {
#overview { #overview {
spacing: 12px; spacing: 12px;
background-color: rgba(0,0,0,0.6);
} }
.window-caption { .window-caption {
@ -511,6 +439,8 @@ StTooltip StLabel {
.dash-placeholder { .dash-placeholder {
background-image: url("dash-placeholder.svg"); background-image: url("dash-placeholder.svg");
height: 27px;
width: 48px;
} }
#viewSelector { #viewSelector {
@ -622,26 +552,27 @@ StTooltip StLabel {
spacing: 12px; 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 { .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; border-radius: 16px;
padding-top: 4px; height: 32px;
padding-bottom: 5px;
width: 300px; width: 300px;
font-weight: bold; font-weight: bold;
} }
.dash-search-button:selected { .dash-search-button:selected,
padding-top: 3px; .dash-search-button:hover {
padding-bottom: 4px; background-gradient-direction: vertical;
width: 298px; background-gradient-start: rgba(255, 255, 255, 0.4);
background-gradient-end: rgba(255, 255, 255, 0.2);
} }
.dash-search-button-label { .dash-search-button-label {
color: white; color: #cccccc;
font-size: 11pt; font-size: 12pt;
} }
/* Apps */ /* Apps */
@ -651,11 +582,6 @@ StTooltip StLabel {
-shell-grid-item-size: 118px; -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-grid .overview-icon {
icon-size: 96px; icon-size: 96px;
} }
@ -722,57 +648,11 @@ StTooltip StLabel {
text-align: center; 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 { .app-well-app.running > .overview-icon {
text-shadow: black 0px 2px 2px; text-shadow: black 0px 2px 2px;
background-image: url("running-indicator.svg"); background-image: url("running-indicator.svg");
} }
.contact:selected,
.app-well-app:selected > .overview-icon, .app-well-app:selected > .overview-icon,
.search-result-content:selected > .overview-icon { .search-result-content:selected > .overview-icon {
background-color: rgba(255,255,255,0.33); background-color: rgba(255,255,255,0.33);
@ -786,7 +666,6 @@ StTooltip StLabel {
transition-duration: 100; transition-duration: 100;
} }
.contact:focus,
.app-well-app:focus > .overview-icon, .app-well-app:focus > .overview-icon,
.search-result-content:focus > .overview-icon { .search-result-content:focus > .overview-icon {
border: 1px solid #cccccc; border: 1px solid #cccccc;
@ -921,15 +800,15 @@ StTooltip StLabel {
/* Calendar popup */ /* Calendar popup */
#calendarEventsArea { #calendarArea {
/* this is the width of the second column of the popup */ /* this is the width of the entire popup */
min-width: 400px; min-width: 600px;
} }
.calendar-vertical-separator { .calendar-vertical-separator {
-stipple-width: 1px; -stipple-width: 1px;
-stipple-color: #505050; -stipple-color: #505050;
width: 0.3em; width: 1.5em;
} }
#calendarPopup { #calendarPopup {
@ -952,14 +831,13 @@ StTooltip StLabel {
.calendar-month-label { .calendar-month-label {
color: #666666; color: #666666;
font-size: 7.5pt; font-size: 7.5pt;
padding-bottom: 8px; padding: 2px;
padding-top: 8px;
font-weight: bold; font-weight: bold;
} }
.calendar-change-month-back { .calendar-change-month-back {
width: 18px; width: 20px;
height: 12px; height: 20px;
background-image: url("calendar-arrow-left.svg"); background-image: url("calendar-arrow-left.svg");
border-radius: 4px; border-radius: 4px;
} }
@ -976,8 +854,8 @@ StTooltip StLabel {
} }
.calendar-change-month-forward { .calendar-change-month-forward {
width: 18px; width: 20px;
height: 12px; height: 20px;
background-image: url("calendar-arrow-right.svg"); background-image: url("calendar-arrow-right.svg");
border-radius: 4px; border-radius: 4px;
} }
@ -1009,7 +887,6 @@ StTooltip StLabel {
.calendar-day-base:hover { .calendar-day-base:hover {
background-color: #777777; background-color: #777777;
color: #fff;
} }
.calendar-day-base:active { .calendar-day-base:active {
@ -1018,7 +895,6 @@ StTooltip StLabel {
.calendar-day-heading { .calendar-day-heading {
color: #666666; color: #666666;
padding-top: 1em;
} }
.calendar-week-number { .calendar-week-number {
@ -1081,8 +957,7 @@ StTooltip StLabel {
font-size: 9pt; font-size: 9pt;
font-weight: bold; font-weight: bold;
color: rgba(153, 153, 153, 1.0); color: rgba(153, 153, 153, 1.0);
padding-left: 1.8em; padding-left: 0.3em;
padding-top: 0.8em;
} }
.events-day-header:rtl { .events-day-header:rtl {
@ -1146,35 +1021,26 @@ StTooltip StLabel {
} }
#notification { #notification {
font-size: 11pt; font-size: 12pt;
border-radius: 10px 10px 0px 0px; border-radius: 5px 5px 0px 0px;
background: rgba(0,0,0,0.8); background: rgba(0,0,0,0.9);
padding: 8px 8px 4px 8px; padding: 8px 8px 4px 8px;
spacing-rows: 10px; spacing-rows: 10px;
spacing-columns: 10px; spacing-columns: 10px;
width: 34em; width: 34em;
} }
#notification.multi-line-notification { .multi-line-notification {
padding-bottom: 8px; 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 { .summary-boxpointer {
-arrow-border-radius: 8px; -arrow-border-radius: 9px;
-arrow-background-color: rgba(0,0,0,0.9); -arrow-background-color: rgba(0,0,0,0.9);
-arrow-border-width: 2px; -arrow-border-width: 2px;
-arrow-border-color: #a5a5a5; -arrow-border-color: #5f5f5f;
-arrow-base: 24px; -arrow-base: 30px;
-arrow-rise: 11px; -arrow-rise: 15px;
color: white;
} }
.summary-boxpointer #notification { .summary-boxpointer #notification {
@ -1228,26 +1094,45 @@ StTooltip StLabel {
} }
#notification-actions { #notification-actions {
spacing: 10px; spacing: 5px;
} }
.notification-button { .notification-button {
border-radius: 18px; background-color: #3c3c3c;
font-size: 11pt; padding: 2px 14px;
padding: 4px 42px 5px; border-radius: 12px;
border: 1px solid #181818;
}
.notification-button:hover {
border: 1px solid #a1a1a1;
} }
.notification-button:focus { .notification-button:focus {
padding: 3px 41px 4px; background-color: #666666;
}
.notification-button:active {
border: 1px solid #a1a1a1;
background-color: #2b2b2b;
} }
.notification-icon-button { .notification-icon-button {
border: 2px rgba(0,0,0,0.0);
border-radius: 5px; border-radius: 5px;
padding: 5px; padding: 5px;
} }
.notification-icon-button:hover {
border: 2px rgba(161,161,161,0.7);
}
.notification-icon-button:focus { .notification-icon-button:focus {
padding: 4px; background: rgba(192,192,192,0.7);
}
.notification-icon-button:active {
background: rgba(128,128,128,0.7);
} }
.notification-icon-button > StIcon { .notification-icon-button > StIcon {
@ -1260,13 +1145,23 @@ StTooltip StLabel {
} }
.hotplug-notification-item { .hotplug-notification-item {
padding: 2px 10px; background-color: #3c3c3c;
border-radius: 18px; padding: 0px 10px;
font-size: 10.5pt; border-radius: 8px;
border: 1px solid #181818;
}
.hotplug-notification-item:hover {
border: 1px solid #a1a1a1;
} }
.hotplug-notification-item:focus { .hotplug-notification-item:focus {
padding: 1px 71px 1px 11px; background-color: #666666;
}
.hotplug-notification-item:active {
border: 1px solid #a1a1a1;
background-color: #2b2b2b;
} }
.hotplug-notification-item-icon { .hotplug-notification-item-icon {
@ -1308,20 +1203,28 @@ StTooltip StLabel {
} }
.hotplug-resident-eject-button { .hotplug-resident-eject-button {
padding: 7px; padding: 2px;
border: 1px solid #2b2b2b;
border-radius: 5px; border-radius: 5px;
color: #ccc; color: #ccc;
} }
.hotplug-resident-eject-button:hover {
color: #fff;
background-color: #2b2b2b;
border: 1px solid #a1a1a1;
}
.chat-log-message { .chat-log-message {
color: #888888; color: #888888;
} }
.chat-group-sent, .chat-group-meta { .chat-received {
padding: 8px 0; background-gradient-direction: horizontal;
} background-gradient-start: rgba(255, 255, 255, 0.2);
background-gradient-end: rgba(255, 255, 255, 0);
.chat-sent {
padding-left: 4px; padding-left: 4px;
border-radius: 4px; border-radius: 4px;
} }
@ -1332,20 +1235,23 @@ StTooltip StLabel {
} }
.chat-sent { .chat-sent {
padding-left: 18pt; background-gradient-direction: horizontal;
background-gradient-start: rgba(255, 255, 255, 0);
background-gradient-end: rgba(255, 255, 255, 0.2);
padding-left: 4px;
border-radius: 4px; border-radius: 4px;
color: #7E7E7E;
} }
.chat-sent:rtl { .chat-sent:rtl {
padding-left: 0px; padding-left: 0px;
padding-right: 18pt; padding-right: 4px;
} }
.chat-meta-message { .chat-meta-message {
padding-left: 4px; padding-left: 4px;
border-radius: 4px; border-radius: 4px;
font-size: 9pt; font-size: 10.5pt;
color: #bbbbbb; color: #bbbbbb;
} }
@ -1354,35 +1260,24 @@ StTooltip StLabel {
padding-right: 4px; padding-right: 4px;
} }
.subscription-message {
font-style: italic;
}
#notification StEntry { #notification StEntry {
padding: 4px; padding: 4px;
border-radius: 4px; border-radius: 4px;
border: 1px solid #565656;
color: #a8a8a8; color: #a8a8a8;
selected-color: black; background-color: #404040;
border: 1px solid rgba(245,245,245,0.2); caret-color: #ffffff;
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; caret-size: 1px;
} }
#notification StEntry:focus { #notification StEntry:focus {
border: 1px solid #8b8b8b; border: 1px solid #3a3a3a;
color: #333333; color: #545454;
background-gradient-direction: vertical; background-color: #e8e8e8;
background-gradient-start: rgb(200,200,200);
background-gradient-end: white;
caret-color: #545454; caret-color: #545454;
selection-background-color: #808080; selection-background-color: #bcbcbc;
selected-color: #323232;
box-shadow: 0px 0px 6px 2px rgba(255,255,255,0.9);
} }
/* The spacing and padding on the summary is tricky; we want to keep /* The spacing and padding on the summary is tricky; we want to keep
@ -1606,7 +1501,7 @@ StTooltip StLabel {
border-radius: 24px; border-radius: 24px;
background-color: rgba(0.0, 0.0, 0.0, 0.9); background-color: rgba(0.0, 0.0, 0.0, 0.9);
border: 2px solid #868686; border: 2px solid #868686;
color: #babdb6; color: #ffffff;
padding-right: 42px; padding-right: 42px;
padding-left: 42px; padding-left: 42px;
@ -1619,21 +1514,37 @@ StTooltip StLabel {
} }
.modal-dialog-button { .modal-dialog-button {
border: 1px solid #8b8b8b;
border-radius: 18px; border-radius: 18px;
font-size: 11pt; font-size: 10.5pt;
color: white;
margin-left: 10px; margin-left: 10px;
margin-right: 10px; margin-right: 10px;
padding: 4px 32px 5px;
padding-left: 32px;
padding-right: 32px;
padding-top: 8px;
padding-bottom: 8px;
background-gradient-direction: vertical;
background-gradient-start: #29323b;
background-gradient-end: #121a24;
} }
.modal-dialog-button:disabled { .modal-dialog-button:active,
color: rgb(60, 60, 60); .modal-dialog-button:pressed {
border-color: #a5a5a5;
background-gradient-start: #121a24;
background-gradient-end: #29323b;
} }
.modal-dialog-button:focus { .modal-dialog-button:focus {
padding: 3px 31px 4px; border: 2px solid #a5a5a5;
padding-left: 31px;
padding-right: 31px;
padding-top: 7px;
padding-bottom: 7px;
} }
/* Run Dialog */ /* Run Dialog */
@ -1895,16 +1806,9 @@ StTooltip StLabel {
} }
.polkit-dialog-password-entry { .polkit-dialog-password-entry {
background-gradient-start: rgb(236,236,236); background-color: white;
background-gradient-end: white;
background-gradient-direction: vertical;
color: black; color: black;
border-radius: 5px; border-radius: 5px;
border: 2px solid #555753;
}
.polkit-dialog-password-entry:focus {
border: 2px solid #3465a4;
} }
.polkit-dialog-error-label { .polkit-dialog-error-label {
@ -1925,17 +1829,6 @@ StTooltip StLabel {
padding-bottom: 8px; 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 */ /* Magnifier */
@ -1999,5 +1892,4 @@ StTooltip StLabel {
-arrow-border-color: white; -arrow-border-color: white;
-arrow-base: 20px; -arrow-base: 20px;
-arrow-rise: 10px; -arrow-rise: 10px;
-boxpointer-gap: 5px;
} }

View File

@ -2,11 +2,6 @@
jsdir = $(pkgdatadir)/js jsdir = $(pkgdatadir)/js
nobase_dist_js_DATA = \ nobase_dist_js_DATA = \
gdm/batch.js \
gdm/consoleKit.js \
gdm/fingerprint.js \
gdm/loginDialog.js \
gdm/powerMenu.js \
misc/config.js \ misc/config.js \
misc/docInfo.js \ misc/docInfo.js \
misc/fileUtils.js \ misc/fileUtils.js \
@ -25,7 +20,7 @@ nobase_dist_js_DATA = \
ui/autorunManager.js \ ui/autorunManager.js \
ui/boxpointer.js \ ui/boxpointer.js \
ui/calendar.js \ ui/calendar.js \
ui/contactDisplay.js \ ui/chrome.js \
ui/ctrlAltTab.js \ ui/ctrlAltTab.js \
ui/dash.js \ ui/dash.js \
ui/dateMenu.js \ ui/dateMenu.js \
@ -45,7 +40,6 @@ nobase_dist_js_DATA = \
ui/main.js \ ui/main.js \
ui/messageTray.js \ ui/messageTray.js \
ui/modalDialog.js \ ui/modalDialog.js \
ui/networkAgent.js \
ui/shellMountOperation.js \ ui/shellMountOperation.js \
ui/notificationDaemon.js \ ui/notificationDaemon.js \
ui/overview.js \ ui/overview.js \
@ -60,6 +54,7 @@ nobase_dist_js_DATA = \
ui/searchDisplay.js \ ui/searchDisplay.js \
ui/shellDBus.js \ ui/shellDBus.js \
ui/statusIconDispatcher.js \ ui/statusIconDispatcher.js \
ui/statusMenu.js \
ui/status/accessibility.js \ ui/status/accessibility.js \
ui/status/keyboard.js \ ui/status/keyboard.js \
ui/status/network.js \ ui/status/network.js \
@ -68,7 +63,6 @@ nobase_dist_js_DATA = \
ui/status/bluetooth.js \ ui/status/bluetooth.js \
ui/telepathyClient.js \ ui/telepathyClient.js \
ui/tweener.js \ ui/tweener.js \
ui/userMenu.js \
ui/viewSelector.js \ ui/viewSelector.js \
ui/windowAttentionHandler.js \ ui/windowAttentionHandler.js \
ui/windowManager.js \ ui/windowManager.js \

View File

@ -1,228 +0,0 @@
/* -*- 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);

View File

@ -1,32 +0,0 @@
// -*- 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);

View File

@ -1,26 +0,0 @@
// -*- 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);

File diff suppressed because it is too large Load Diff

View File

@ -1,146 +0,0 @@
/* -*- 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,5 +7,4 @@ const PACKAGE_VERSION = '@PACKAGE_VERSION@';
const GJS_VERSION = '@GJS_VERSION@'; const GJS_VERSION = '@GJS_VERSION@';
/* 1 if gnome-bluetooth is available, 0 otherwise */ /* 1 if gnome-bluetooth is available, 0 otherwise */
const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@; const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@;
/* The system TLS CA list */
const SHELL_SYSTEM_CA_FILE = '@SHELL_SYSTEM_CA_FILE@';

View File

@ -20,25 +20,3 @@ function listDirAsync(file, callback) {
enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, onNextFileComplete); 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,32 +7,8 @@ const Shell = imports.gi.Shell;
const Main = imports.ui.main; const Main = imports.ui.main;
// http://daringfireball.net/2010/07/improved_regex_for_matching_urls /* http://daringfireball.net/2010/07/improved_regex_for_matching_urls */
const _balancedParens = '\\((?:[^\\s()<>]+|(?:\\(?:[^\\s()<>]+\\)))*\\)'; 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');
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: // findUrls:
// @str: string to find URLs in // @str: string to find URLs in
@ -45,7 +21,7 @@ const _urlRegexp = new RegExp(
function findUrls(str) { function findUrls(str) {
let res = [], match; let res = [], match;
while ((match = _urlRegexp.exec(str))) while ((match = _urlRegexp.exec(str)))
res.push({ url: match[2], pos: match.index + match[1].length }); res.push({ url: match[0], pos: match.index });
return res; return res;
} }

View File

@ -113,10 +113,10 @@ function run() {
for (let i = 0; i < 2; i++) { for (let i = 0; i < 2; i++) {
Scripting.scriptEvent('applicationsShowStart'); Scripting.scriptEvent('applicationsShowStart');
Main.overview._viewSelector.switchTab('applications'); Main.overview.viewSelector.switchTab('applications');
yield Scripting.waitLeisure(); yield Scripting.waitLeisure();
Scripting.scriptEvent('applicationsShowDone'); Scripting.scriptEvent('applicationsShowDone');
Main.overview._viewSelector.switchTab('windows'); Main.overview.viewSelector.switchTab('windows');
yield Scripting.waitLeisure(); yield Scripting.waitLeisure();
} }
} }

View File

@ -14,7 +14,7 @@ const Tweener = imports.ui.tweener;
const POPUP_APPICON_SIZE = 96; const POPUP_APPICON_SIZE = 96;
const POPUP_SCROLL_TIME = 0.10; // seconds const POPUP_SCROLL_TIME = 0.10; // seconds
const POPUP_DELAY_TIMEOUT = 150; // milliseconds const POPUP_FADE_IN_TIME = 0.4; // seconds
const POPUP_FADE_OUT_TIME = 0.1; // seconds const POPUP_FADE_OUT_TIME = 0.1; // seconds
const APP_ICON_HOVER_TIMEOUT = 200; // milliseconds const APP_ICON_HOVER_TIMEOUT = 200; // milliseconds
@ -31,18 +31,6 @@ function mod(a, b) {
return (a + b) % 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() { function AltTabPopup() {
this._init(); this._init();
} }
@ -60,13 +48,11 @@ AltTabPopup.prototype = {
this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._haveModal = false; this._haveModal = false;
this._modifierMask = 0;
this._currentApp = 0; this._currentApp = 0;
this._currentWindow = -1; this._currentWindow = -1;
this._thumbnailTimeoutId = 0; this._thumbnailTimeoutId = 0;
this._motionTimeoutId = 0; this._motionTimeoutId = 0;
this._initialDelayTimeoutId = 0;
this.thumbnailsVisible = false; this.thumbnailsVisible = false;
@ -134,9 +120,9 @@ AltTabPopup.prototype = {
} }
}, },
show : function(backward, binding, mask) { show : function(backward, binding) {
let appSys = Shell.AppSystem.get_default(); let tracker = Shell.WindowTracker.get_default();
let apps = appSys.get_running (); let apps = tracker.get_running_apps ('');
if (!apps.length) if (!apps.length)
return false; return false;
@ -144,7 +130,6 @@ AltTabPopup.prototype = {
if (!Main.pushModal(this.actor)) if (!Main.pushModal(this.actor))
return false; return false;
this._haveModal = true; this._haveModal = true;
this._modifierMask = primaryModifier(mask);
this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent)); this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent)); this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
@ -193,18 +178,21 @@ AltTabPopup.prototype = {
// details.) So we check now. (Have to do this after updating // details.) So we check now. (Have to do this after updating
// selection.) // selection.)
let [x, y, mods] = global.get_pointer(); let [x, y, mods] = global.get_pointer();
if (!(mods & this._modifierMask)) { if (!(mods & Gdk.ModifierType.MOD1_MASK)) {
this._finish(); this._finish();
return false; return false;
} }
// We delay showing the popup so that fast Alt+Tab users aren't // Using easeInOutExpo over 400ms gives us 150ms of "nearly
// disturbed by the popup briefly flashing. // invisible" (less than 10% opacity), followed by a 100ms
this._initialDelayTimeoutId = Mainloop.timeout_add(POPUP_DELAY_TIMEOUT, // tween in (to 90% opacity, with the last 10% coming over the
Lang.bind(this, function () { // next 150ms). So if the user releases Alt quickly after we
this.actor.opacity = 255; // start tweening, they'll never see the switcher.
this._initialDelayTimeoutId = 0; Tweener.addTween(this.actor,
})); { opacity: 255,
time: POPUP_FADE_IN_TIME,
transition: 'easeInOutExpo'
});
return true; return true;
}, },
@ -235,7 +223,7 @@ AltTabPopup.prototype = {
let keysym = event.get_key_symbol(); let keysym = event.get_key_symbol();
let event_state = Shell.get_event_state(event); let event_state = Shell.get_event_state(event);
let backwards = event_state & Clutter.ModifierType.SHIFT_MASK; let backwards = event_state & Clutter.ModifierType.SHIFT_MASK;
let action = global.display.get_keybinding_action(event.get_key_code(), event_state); let action = global.screen.get_display().get_keybinding_action(event.get_key_code(), event_state);
this._disableHover(); this._disableHover();
@ -270,7 +258,7 @@ AltTabPopup.prototype = {
_keyReleaseEvent : function(actor, event) { _keyReleaseEvent : function(actor, event) {
let [x, y, mods] = global.get_pointer(); let [x, y, mods] = global.get_pointer();
let state = mods & this._modifierMask; let state = mods & Clutter.ModifierType.MOD1_MASK;
if (state == 0) if (state == 0)
this._finish(); this._finish();
@ -407,8 +395,6 @@ AltTabPopup.prototype = {
Mainloop.source_remove(this._motionTimeoutId); Mainloop.source_remove(this._motionTimeoutId);
if (this._thumbnailTimeoutId != 0) if (this._thumbnailTimeoutId != 0)
Mainloop.source_remove(this._thumbnailTimeoutId); Mainloop.source_remove(this._thumbnailTimeoutId);
if (this._initialDelayTimeoutId != 0)
Mainloop.source_remove(this._initialDelayTimeoutId);
}, },
/** /**
@ -495,10 +481,6 @@ AltTabPopup.prototype = {
this.actor.add_actor(this._thumbnails.actor); 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; this._thumbnails.actor.opacity = 0;
Tweener.addTween(this._thumbnails.actor, Tweener.addTween(this._thumbnails.actor,
{ opacity: 255, { opacity: 255,
@ -650,10 +632,11 @@ SwitcherList.prototype = {
this._items[this._highlighted].add_style_pseudo_class('selected'); this._items[this._highlighted].add_style_pseudo_class('selected');
} }
let [absItemX, absItemY] = this._items[index].get_transformed_position(); let monitor = Main.layoutManager.primaryMonitor;
let [result, posX, posY] = this.actor.transform_stage_point(absItemX, 0); let itemSize = this._items[index].allocation.x2 - this._items[index].allocation.x1;
let [containerWidth, containerHeight] = this.actor.get_transformed_size(); let [posX, posY] = this._items[index].get_transformed_position();
if (posX + this._items[index].get_width() > containerWidth) posX += this.actor.x;
if (posX + itemSize > monitor.width + monitor.x)
this._scrollToRight(); this._scrollToRight();
else if (posX < 0) else if (posX < 0)
this._scrollToLeft(); this._scrollToLeft();

View File

@ -3,7 +3,6 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk; const Gtk = imports.gi.Gtk;
const GMenu = imports.gi.GMenu;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Lang = imports.lang; const Lang = imports.lang;
const Signals = imports.signals; const Signals = imports.signals;
@ -36,7 +35,8 @@ AlphabeticalView.prototype = {
this._appSystem = Shell.AppSystem.get_default(); this._appSystem = Shell.AppSystem.get_default();
this._pendingAppLaterId = 0; this._pendingAppLaterId = 0;
this._appIcons = {}; // desktop file id this._apps = [];
this._filterApp = null;
let box = new St.BoxLayout({ vertical: true }); let box = new St.BoxLayout({ vertical: true });
box.add(this._grid.actor, { y_align: St.Align.START, expand: true }); box.add(this._grid.actor, { y_align: St.Align.START, expand: true });
@ -63,17 +63,20 @@ AlphabeticalView.prototype = {
_removeAll: function() { _removeAll: function() {
this._grid.removeAll(); this._grid.removeAll();
this._appIcons = {}; this._apps = [];
}, },
_addApp: function(app) { _addApp: function(appInfo) {
var id = app.get_id(); let appIcon = new AppWellIcon(this._appSystem.get_app(appInfo.get_id()));
let appIcon = new AppWellIcon(app);
this._grid.addItem(appIcon.actor); this._grid.addItem(appIcon.actor);
appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible)); appIcon.actor.connect('key-focus-in', Lang.bind(this, this._ensureIconVisible));
this._appIcons[id] = appIcon; appIcon._appInfo = appInfo;
if (this._filterApp && !this._filterApp(appInfo))
appIcon.actor.hide();
this._apps.push(appIcon);
}, },
_ensureIconVisible: function(icon) { _ensureIconVisible: function(icon) {
@ -102,33 +105,52 @@ AlphabeticalView.prototype = {
transition: 'easeOutQuad' }); transition: 'easeOutQuad' });
}, },
setVisibleApps: function(apps) { setFilter: function(filter) {
if (apps == null) { // null implies "all" this._filterApp = filter;
for (var id in this._appIcons) { for (let i = 0; i < this._apps.length; i++)
var icon = this._appIcons[id]; this._apps[i].actor.visible = filter(this._apps[i]._appInfo);
icon.actor.visible = true; },
}
// 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;
} else { } else {
// Set everything to not-visible, then set to visible what we should see this._pendingAppLaterId = 0;
for (var id in this._appIcons) { this._pendingAppIds = null;
var icon = this._appIcons[id]; this._pendingApps = null;
icon.actor.visible = false; return 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;
}
} }
}, },
setAppList: function(apps) { 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());
});
this._removeAll(); this._removeAll();
for (var i = 0; i < apps.length; i++) {
var app = apps[i]; this._pendingAppIds = ids;
this._addApp(app); 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));
} }
}; };
@ -149,24 +171,23 @@ ViewByCategories.prototype = {
// -2 is a flag to indicate that nothing is selected // -2 is a flag to indicate that nothing is selected
// (used only before the actor is mapped the first time) // (used only before the actor is mapped the first time)
this._currentCategory = -2; this._currentCategory = -2;
this._categories = []; this._filters = new St.BoxLayout({ vertical: true, reactive: true });
this._apps = null; this._filtersBox = new St.ScrollView({ x_fill: false,
y_fill: false,
this._categoryBox = new St.BoxLayout({ vertical: true, reactive: true }); style_class: 'vfade' });
this._categoryScroll = new St.ScrollView({ x_fill: false, this._filtersBox.add_actor(this._filters);
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._view.actor, { expand: true, x_fill: true, y_fill: true });
this.actor.add(this._categoryScroll, { expand: false, y_fill: false, y_align: St.Align.START }); this.actor.add(this._filtersBox, { expand: false, y_fill: false, y_align: St.Align.START });
// Always select the "All" filter when switching to the app view // Always select the "All" filter when switching to the app view
this.actor.connect('notify::mapped', Lang.bind(this, this.actor.connect('notify::mapped', Lang.bind(this,
function() { function() {
if (this.actor.mapped && this._allCategoryButton) if (this.actor.mapped && this._allFilter)
this._selectCategory(-1); this._selectCategory(-1);
})); }));
this._sections = [];
// We need a dummy actor to catch the keyboard focus if the // We need a dummy actor to catch the keyboard focus if the
// user Ctrl-Alt-Tabs here before the deferred work creates // user Ctrl-Alt-Tabs here before the deferred work creates
// our real contents // our real contents
@ -180,98 +201,64 @@ ViewByCategories.prototype = {
this._currentCategory = num; this._currentCategory = num;
if (num != -1) { if (num != -1)
var category = this._categories[num]; this._allFilter.remove_style_pseudo_class('selected');
this._allCategoryButton.remove_style_pseudo_class('selected'); else
this._view.setVisibleApps(category.apps); this._allFilter.add_style_pseudo_class('selected');
} else {
this._allCategoryButton.add_style_pseudo_class('selected');
this._view.setVisibleApps(null);
}
for (var i = 0; i < this._categories.length; i++) { 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++) {
if (i == num) if (i == num)
this._categories[i].button.add_style_pseudo_class('selected'); this._sections[i].filterActor.add_style_pseudo_class('selected');
else else
this._categories[i].button.remove_style_pseudo_class('selected'); this._sections[i].filterActor.remove_style_pseudo_class('selected');
} }
}, },
// Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too _addFilter: function(name, num) {
_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), let button = new St.Button({ label: GLib.markup_escape_text (name, -1),
style_class: 'app-filter', style_class: 'app-filter',
x_align: St.Align.START, x_align: St.Align.START,
can_focus: true }); can_focus: true });
this._filters.add(button, { expand: true, x_fill: true, y_fill: false });
button.connect('clicked', Lang.bind(this, function() { button.connect('clicked', Lang.bind(this, function() {
this._selectCategory(index); this._selectCategory(num);
})); }));
var apps; if (num != -1)
if (dir == null) { this._sections[num] = { filterActor: button,
apps = allApps; name: name };
this._allCategoryButton = button; else
} else { this._allFilter = button;
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() { _removeAll: function() {
this._categories = []; this._sections = [];
this._categoryBox.destroy_children(); this._filters.destroy_children();
}, },
refresh: function() { refresh: function(apps) {
this._removeAll(); this._removeAll();
var allApps = Shell.AppSystem.get_default().get_all(); let sections = this._appSystem.get_sections();
allApps.sort(function(a, b) { this._apps = apps;
return a.compare_by_name(b);
});
/* Translators: Filter to display all applications */ /* Translators: Filter to display all applications */
this._addCategory(_("All"), -1, null, allApps); this._addFilter(_("All"), -1);
var tree = this._appSystem.get_tree(); if (!sections)
var root = tree.get_root_directory(); return;
var iter = root.iter(); for (let i = 0; i < sections.length; i++)
var nextType; this._addFilter(sections[i], i);
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._selectCategory(-1);
this._view.refresh(apps);
if (this._focusDummy) { if (this._focusDummy) {
let focused = this._focusDummy.has_key_focus(); let focused = this._focusDummy.has_key_focus();
@ -304,7 +291,60 @@ AllAppDisplay.prototype = {
}, },
_redisplay: function() { _redisplay: function() {
this._appView.refresh(); 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);
} }
}; };
@ -313,104 +353,44 @@ function AppSearchProvider() {
} }
AppSearchProvider.prototype = { AppSearchProvider.prototype = {
__proto__: Search.SearchProvider.prototype, __proto__: BaseAppSearchProvider.prototype,
_init: function() { _init: function() {
Search.SearchProvider.prototype._init.call(this, _("APPLICATIONS")); BaseAppSearchProvider.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) { getInitialResultSet: function(terms) {
return this._appSys.initial_search(terms); return this._appSys.initial_search(false, terms);
}, },
getSubsearchResultSet: function(previousResults, terms) { getSubsearchResultSet: function(previousResults, terms) {
return this._appSys.subsearch(previousResults, terms); return this._appSys.subsearch(false, 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) { createResultActor: function (resultMeta, terms) {
let app = resultMeta['id']; let app = this._appSys.get_app(resultMeta['id']);
let icon = new AppWellIcon(app); let icon = new AppWellIcon(app);
return icon.actor; return icon.actor;
} }
}; };
function SettingsSearchProvider() { function PrefsSearchProvider() {
this._init(); this._init();
} }
SettingsSearchProvider.prototype = { PrefsSearchProvider.prototype = {
__proto__: Search.SearchProvider.prototype, __proto__: BaseAppSearchProvider.prototype,
_init: function() { _init: function() {
Search.SearchProvider.prototype._init.call(this, _("SETTINGS")); BaseAppSearchProvider.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) { getInitialResultSet: function(terms) {
return this._appSys.search_settings(terms); return this._appSys.initial_search(true, terms);
}, },
getSubsearchResultSet: function(previousResults, terms) { getSubsearchResultSet: function(previousResults, terms) {
return this._appSys.search_settings(terms); return this._appSys.subsearch(true, previousResults, 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;
} }
}; };
@ -436,12 +416,12 @@ AppIcon.prototype = {
} }
}; };
function AppWellIcon(app, iconParams, onActivateOverride) { function AppWellIcon(app, iconParams) {
this._init(app, iconParams, onActivateOverride); this._init(app, iconParams);
} }
AppWellIcon.prototype = { AppWellIcon.prototype = {
_init : function(app, iconParams, onActivateOverride) { _init : function(app, iconParams) {
this.app = app; this.app = app;
this.actor = new St.Button({ style_class: 'app-well-app', this.actor = new St.Button({ style_class: 'app-well-app',
reactive: true, reactive: true,
@ -456,8 +436,6 @@ AppWellIcon.prototype = {
this.actor.label_actor = this.icon.label; 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('button-press-event', Lang.bind(this, this._onButtonPress));
this.actor.connect('clicked', Lang.bind(this, this._onClicked)); this.actor.connect('clicked', Lang.bind(this, this._onClicked));
this.actor.connect('popup-menu', Lang.bind(this, this._onKeyboardPopupMenu)); this.actor.connect('popup-menu', Lang.bind(this, this._onKeyboardPopupMenu));
@ -559,7 +537,7 @@ AppWellIcon.prototype = {
this._menu.connect('activate-window', Lang.bind(this, function (menu, window) { this._menu.connect('activate-window', Lang.bind(this, function (menu, window) {
this.activateWindow(window); this.activateWindow(window);
})); }));
this._menu.connect('open-state-changed', Lang.bind(this, function (menu, isPoppedUp) { this._menu.connect('popup', Lang.bind(this, function (menu, isPoppedUp) {
if (!isPoppedUp) if (!isPoppedUp)
this._onMenuPoppedDown(); this._onMenuPoppedDown();
})); }));
@ -591,28 +569,24 @@ AppWellIcon.prototype = {
this.emit('launching'); this.emit('launching');
let modifiers = Shell.get_event_state(event); let modifiers = Shell.get_event_state(event);
if (this._onActivateOverride) { if (modifiers & Clutter.ModifierType.CONTROL_MASK
this._onActivateOverride(event); && this.app.state == Shell.AppState.RUNNING) {
this.app.open_new_window(-1);
} else { } else {
if (modifiers & Clutter.ModifierType.CONTROL_MASK this.app.activate(-1);
&& this.app.state == Shell.AppState.RUNNING) {
this.app.open_new_window(-1);
} else {
this.app.activate();
}
} }
Main.overview.hide(); Main.overview.hide();
}, },
shellWorkspaceLaunch : function(params) { shellWorkspaceLaunch : function(params) {
params = Params.parse(params, { workspace: -1, params = Params.parse(params, { workspace: null,
timestamp: 0 }); timestamp: null });
this.app.open_new_window(params.workspace); this.app.open_new_window(params.workspace ? params.workspace.index() : -1);
}, },
getDragActor: function() { getDragActor: function() {
return this.app.create_icon_texture(Main.overview.dashIconSize); return this.app.create_icon_texture(Main.overview.dash.iconSize);
}, },
// Returns the original actor that should align with the actor // Returns the original actor that should align with the actor
@ -635,7 +609,7 @@ AppIconMenu.prototype = {
if (St.Widget.get_default_direction() == St.TextDirection.RTL) if (St.Widget.get_default_direction() == St.TextDirection.RTL)
side = St.Side.RIGHT; side = St.Side.RIGHT;
PopupMenu.PopupMenu.prototype._init.call(this, source.actor, 0.5, side); PopupMenu.PopupMenu.prototype._init.call(this, source.actor, 0.5, side, 0);
// We want to keep the item hovered while the menu is up // We want to keep the item hovered while the menu is up
this.blockSourceEvents = true; this.blockSourceEvents = true;
@ -643,6 +617,7 @@ AppIconMenu.prototype = {
this._source = source; this._source = source;
this.connect('activate', Lang.bind(this, this._onActivate)); 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'); this.actor.add_style_class_name('app-well-menu');
@ -675,18 +650,17 @@ AppIconMenu.prototype = {
item._window = windows[i]; item._window = windows[i];
} }
if (!this._source.app.is_window_backed()) { if (windows.length > 0)
if (windows.length > 0)
this._appendSeparator();
let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
this._appendSeparator(); this._appendSeparator();
this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites") let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
: _("Add to Favorites"));
} this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
this._appendSeparator();
this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
: _("Add to Favorites"));
}, },
_appendSeparator: function () { _appendSeparator: function () {
@ -706,6 +680,14 @@ AppIconMenu.prototype = {
this.open(); this.open();
}, },
_onOpenStateChanged: function (menu, open) {
if (open) {
this.emit('popup', true);
} else {
this.emit('popup', false);
}
},
_onActivate: function (actor, child) { _onActivate: function (actor, child) {
if (child._window) { if (child._window) {
let metaWindow = 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 ids = global.settings.get_strv(this.FAVORITE_APPS_KEY);
let appSys = Shell.AppSystem.get_default(); let appSys = Shell.AppSystem.get_default();
let apps = ids.map(function (id) { let apps = ids.map(function (id) {
return appSys.lookup_app(id); return appSys.get_app(id);
}).filter(function (app) { }).filter(function (app) {
return app != null; return app != null;
}); });
@ -65,7 +65,7 @@ AppFavorites.prototype = {
if (appId in this._favorites) if (appId in this._favorites)
return false; return false;
let app = Shell.AppSystem.get_default().lookup_app(appId); let app = Shell.AppSystem.get_default().get_app(appId);
if (!app) if (!app)
return false; return false;
@ -84,9 +84,9 @@ AppFavorites.prototype = {
if (!this._addFavorite(appId, pos)) if (!this._addFavorite(appId, pos))
return; return;
let app = Shell.AppSystem.get_default().lookup_app(appId); let app = Shell.AppSystem.get_default().get_app(appId);
Main.overview.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () { Main.overview.shellInfo.setMessage(_("%s has been added to your favorites.").format(app.get_name()), Lang.bind(this, function () {
this._removeFavorite(appId); this._removeFavorite(appId);
})); }));
}, },
@ -117,8 +117,8 @@ AppFavorites.prototype = {
if (!this._removeFavorite(appId)) if (!this._removeFavorite(appId))
return; return;
Main.overview.setMessage(_("%s has been removed from your favorites.").format(app.get_name()), Main.overview.shellInfo.setMessage(_("%s has been removed from your favorites.").format(app.get_name()),
Lang.bind(this, function () { Lang.bind(this, function () {
this._addFavorite(appId, pos); this._addFavorite(appId, pos);
})); }));
} }

View File

@ -23,6 +23,8 @@ const AutorunSetting = {
ASK: 3 ASK: 3
}; };
const HOTPLUG_ICON_SIZE = 16;
// misc utils // misc utils
function ignoreAutorunForMount(mount) { function ignoreAutorunForMount(mount) {
let root = mount.get_root(); let root = mount.get_root();
@ -277,12 +279,12 @@ AutorunResidentSource.prototype = {
__proto__: MessageTray.Source.prototype, __proto__: MessageTray.Source.prototype,
_init: function() { _init: function() {
MessageTray.Source.prototype._init.call(this, _("Removable Devices")); MessageTray.Source.prototype._init.call(this, _('Removable Devices'));
this._mounts = []; this._mounts = [];
this._notification = new AutorunResidentNotification(this); this._notification = new AutorunResidentNotification(this);
this._setSummaryIcon(this.createNotificationIcon()); this._setSummaryIcon(this.createNotificationIcon(HOTPLUG_ICON_SIZE));
}, },
addMount: function(mount, apps) { addMount: function(mount, apps) {
@ -327,10 +329,9 @@ AutorunResidentSource.prototype = {
} }
}, },
createNotificationIcon: function() { createNotificationIcon: function(iconSize) {
return new St.Icon ({ icon_name: 'media-removable', return new St.Icon ({ icon_name: 'drive-harddisk',
icon_type: St.IconType.FULLCOLOR, icon_size: iconSize ? iconSize : this.ICON_SIZE });
icon_size: this.ICON_SIZE });
} }
} }
@ -531,16 +532,16 @@ AutorunTransientSource.prototype = {
this.apps = apps; this.apps = apps;
this._notification = new AutorunTransientNotification(this); this._notification = new AutorunTransientNotification(this);
this._setSummaryIcon(this.createNotificationIcon()); this._setSummaryIcon(this.createNotificationIcon(this.ICON_SIZE));
// add ourselves as a source, and popup the notification // add ourselves as a source, and popup the notification
Main.messageTray.add(this); Main.messageTray.add(this);
this.notify(this._notification); this.notify(this._notification);
}, },
createNotificationIcon: function() { createNotificationIcon: function(iconSize) {
return new St.Icon({ gicon: this.mount.get_icon(), return new St.Icon({ gicon: this.mount.get_icon(),
icon_size: this.ICON_SIZE }); icon_size: iconSize ? iconSize : this.ICON_SIZE });
} }
} }

View File

@ -180,7 +180,7 @@ BoxPointer.prototype = {
this.bin.allocate(childBox, flags); this.bin.allocate(childBox, flags);
if (this._sourceActor && this._sourceActor.mapped) if (this._sourceActor && this._sourceActor.mapped)
this._reposition(this._sourceActor, this._alignment); this._reposition(this._sourceActor, this._gap, this._alignment);
}, },
_drawBorder: function(area) { _drawBorder: function(area) {
@ -306,18 +306,19 @@ BoxPointer.prototype = {
cr.stroke(); cr.stroke();
}, },
setPosition: function(sourceActor, alignment) { setPosition: function(sourceActor, gap, alignment) {
// We need to show it now to force an allocation, // We need to show it now to force an allocation,
// so that we can query the correct size. // so that we can query the correct size.
this.actor.show(); this.actor.show();
this._sourceActor = sourceActor; this._sourceActor = sourceActor;
this._gap = gap;
this._alignment = alignment; this._alignment = alignment;
this._reposition(sourceActor, alignment); this._reposition(sourceActor, gap, alignment);
}, },
_reposition: function(sourceActor, alignment) { _reposition: function(sourceActor, gap, alignment) {
// Position correctly relative to the sourceActor // Position correctly relative to the sourceActor
let sourceNode = sourceActor.get_theme_node(); let sourceNode = sourceActor.get_theme_node();
let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box()); let sourceContentBox = sourceNode.get_content_box(sourceActor.get_allocation_box());
@ -337,9 +338,6 @@ BoxPointer.prototype = {
let margin = (4 * borderRadius + borderWidth + arrowBase); let margin = (4 * borderRadius + borderWidth + arrowBase);
let halfMargin = margin / 2; let halfMargin = margin / 2;
let themeNode = this.actor.get_theme_node();
let gap = themeNode.get_length('-boxpointer-gap');
let resX, resY; let resX, resY;
switch (this._arrowSide) { switch (this._arrowSide) {

View File

@ -351,16 +351,17 @@ function Calendar(eventSource) {
Calendar.prototype = { Calendar.prototype = {
_init: function(eventSource) { _init: function(eventSource) {
if (eventSource) { this._eventSource = eventSource;
this._eventSource = eventSource;
this._eventSource.connect('changed', Lang.bind(this, this._eventSource.connect('changed', Lang.bind(this,
function() { function() {
this._update(false); this._update(false);
})); }));
}
this._weekStart = Shell.util_get_week_start(); // 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._weekdate = NaN; this._weekdate = NaN;
this._digitWidth = NaN; this._digitWidth = NaN;
this._settings = new Gio.Settings({ schema: 'org.gnome.shell.calendar' }); this._settings = new Gio.Settings({ schema: 'org.gnome.shell.calendar' });
@ -368,6 +369,16 @@ Calendar.prototype = {
this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange)); this._settings.connect('changed::' + SHOW_WEEKDATE_KEY, Lang.bind(this, this._onSettingsChange));
this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY); 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 // Find the ordering for month/year in the calendar heading
this._headerFormatWithoutYear = '%B'; this._headerFormatWithoutYear = '%B';
switch (Gettext_gtk30.gettext('calendar:MY')) { switch (Gettext_gtk30.gettext('calendar:MY')) {
@ -556,16 +567,13 @@ Calendar.prototype = {
while (true) { while (true) {
let button = new St.Button({ label: iter.getDate().toString() }); let button = new St.Button({ label: iter.getDate().toString() });
if (!this._eventSource)
button.reactive = false;
let iterStr = iter.toUTCString(); let iterStr = iter.toUTCString();
button.connect('clicked', Lang.bind(this, function() { button.connect('clicked', Lang.bind(this, function() {
let newlySelectedDate = new Date(iterStr); let newlySelectedDate = new Date(iterStr);
this.setDate(newlySelectedDate, false); this.setDate(newlySelectedDate, false);
})); }));
let hasEvents = this._eventSource && this._eventSource.hasEvents(iter); let hasEvents = this._eventSource.hasEvents(iter);
let styleClass = 'calendar-day-base calendar-day'; let styleClass = 'calendar-day-base calendar-day';
if (_isWorkDay(iter)) if (_isWorkDay(iter))
styleClass += ' calendar-work-day' styleClass += ' calendar-work-day'
@ -612,8 +620,7 @@ Calendar.prototype = {
} }
// Signal to the event source that we are interested in events // Signal to the event source that we are interested in events
// only from this date range // only from this date range
if (this._eventSource) this._eventSource.requestRange(beginDate, iter, forceReload);
this._eventSource.requestRange(beginDate, iter, forceReload);
} }
}; };
@ -631,7 +638,17 @@ EventsList.prototype = {
this._eventSource.connect('changed', Lang.bind(this, this._update)); this._eventSource.connect('changed', Lang.bind(this, this._update));
this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' }); this._desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' });
this._desktopSettings.connect('changed', Lang.bind(this, this._update)); this._desktopSettings.connect('changed', Lang.bind(this, this._update));
this._weekStart = Shell.util_get_week_start(); 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._update(); this._update();
}, },
@ -650,9 +667,6 @@ EventsList.prototype = {
}, },
_addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) { _addPeriod: function(header, begin, end, includeDayName, showNothingScheduled) {
if (!this._eventSource)
return;
let events = this._eventSource.getEvents(begin, end); let events = this._eventSource.getEvents(begin, end);
let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);; let clockFormat = this._desktopSettings.get_string(CLOCK_FORMAT_KEY);;

445
js/ui/chrome.js Normal file
View File

@ -0,0 +1,445 @@
/* -*- 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.connect('initialized', Lang.bind(this, this._finishInit));
},
_finishInit: function() {
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);

View File

@ -1,179 +0,0 @@
/* -*- 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; return a.x - b.x;
}, },
popup: function(backwards, mask) { popup: function(backwards) {
// Start with the set of focus groups that are currently mapped // Start with the set of focus groups that are currently mapped
let items = this._items.filter(function (item) { return item.proxy.mapped; }); let items = this._items.filter(function (item) { return item.proxy.mapped; });
@ -123,16 +123,7 @@ CtrlAltTabManager.prototype = {
return; return;
items.sort(Lang.bind(this, this._sortItems)); 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;
}));
}
} }
}; };
@ -156,7 +147,6 @@ CtrlAltTabPopup.prototype = {
this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._haveModal = false; this._haveModal = false;
this._modifierMask = 0;
this._selection = 0; this._selection = 0;
Main.uiGroup.add_actor(this.actor); Main.uiGroup.add_actor(this.actor);
@ -193,11 +183,10 @@ CtrlAltTabPopup.prototype = {
this._switcher.actor.allocate(childBox, flags); this._switcher.actor.allocate(childBox, flags);
}, },
show : function(items, startBackwards, mask) { show : function(items, startBackwards) {
if (!Main.pushModal(this.actor)) if (!Main.pushModal(this.actor))
return false; return false;
this._haveModal = true; this._haveModal = true;
this._modifierMask = AltTab.primaryModifier(mask);
this._keyPressEventId = this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent)); this._keyPressEventId = this.actor.connect('key-press-event', Lang.bind(this, this._keyPressEvent));
this._keyReleaseEventId = this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent)); this._keyReleaseEventId = this.actor.connect('key-release-event', Lang.bind(this, this._keyReleaseEvent));
@ -211,7 +200,7 @@ CtrlAltTabPopup.prototype = {
this._select(this._selection); this._select(this._selection);
let [x, y, mods] = global.get_pointer(); let [x, y, mods] = global.get_pointer();
if (!(mods & this._modifierMask)) { if (!(mods & Gdk.ModifierType.MOD1_MASK)) {
this._finish(); this._finish();
return false; return false;
} }
@ -257,7 +246,7 @@ CtrlAltTabPopup.prototype = {
_keyReleaseEvent : function(actor, event) { _keyReleaseEvent : function(actor, event) {
let [x, y, mods] = global.get_pointer(); let [x, y, mods] = global.get_pointer();
let state = mods & this._modifierMask; let state = mods & Clutter.ModifierType.MOD1_MASK;
if (state == 0) if (state == 0)
this._finish(); this._finish();

View File

@ -207,7 +207,7 @@ RemoveFavoriteIcon.prototype = {
let app = null; let app = null;
if (source instanceof AppDisplay.AppWellIcon) { if (source instanceof AppDisplay.AppWellIcon) {
let appSystem = Shell.AppSystem.get_default(); let appSystem = Shell.AppSystem.get_default();
app = appSystem.lookup_app(source.getId()); app = appSystem.get_app(source.getId());
} else if (source.metaWindow) { } else if (source.metaWindow) {
let tracker = Shell.WindowTracker.get_default(); let tracker = Shell.WindowTracker.get_default();
app = tracker.get_window_app(source.metaWindow); app = tracker.get_window_app(source.metaWindow);
@ -275,7 +275,7 @@ Dash.prototype = {
this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay)); this._appSystem.connect('installed-changed', Lang.bind(this, this._queueRedisplay));
AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay)); AppFavorites.getAppFavorites().connect('changed', Lang.bind(this, this._queueRedisplay));
this._appSystem.connect('app-state-changed', Lang.bind(this, this._queueRedisplay)); this._tracker.connect('app-state-changed', Lang.bind(this, this._queueRedisplay));
Main.overview.connect('item-drag-begin', Main.overview.connect('item-drag-begin',
Lang.bind(this, this._onDragBegin)); Lang.bind(this, this._onDragBegin));
@ -330,7 +330,7 @@ Dash.prototype = {
_onDragMotion: function(dragEvent) { _onDragMotion: function(dragEvent) {
let app = null; let app = null;
if (dragEvent.source instanceof AppDisplay.AppWellIcon) if (dragEvent.source instanceof AppDisplay.AppWellIcon)
app = this._appSystem.lookup_app(dragEvent.source.getId()); app = this._appSystem.get_app(dragEvent.source.getId());
else if (dragEvent.source.metaWindow) else if (dragEvent.source.metaWindow)
app = this._tracker.get_window_app(dragEvent.source.metaWindow); app = this._tracker.get_window_app(dragEvent.source.metaWindow);
else else
@ -437,7 +437,6 @@ Dash.prototype = {
let oldIconSize = this.iconSize; let oldIconSize = this.iconSize;
this.iconSize = newIconSize; this.iconSize = newIconSize;
this.emit('icon-size-changed');
let scale = oldIconSize / newIconSize; let scale = oldIconSize / newIconSize;
for (let i = 0; i < iconChildren.length; i++) { for (let i = 0; i < iconChildren.length; i++) {
@ -471,7 +470,10 @@ Dash.prototype = {
_redisplay: function () { _redisplay: function () {
let favorites = AppFavorites.getAppFavorites().getFavoriteMap(); let favorites = AppFavorites.getAppFavorites().getFavoriteMap();
let running = this._appSystem.get_running(); /* hardcode here pending some design about how exactly desktop contexts behave */
let contextId = '';
let running = this._tracker.get_running_apps(contextId);
let children = this._box.get_children().filter(function(actor) { let children = this._box.get_children().filter(function(actor) {
return actor._delegate.child && return actor._delegate.child &&
@ -617,12 +619,12 @@ Dash.prototype = {
handleDragOver : function(source, actor, x, y, time) { handleDragOver : function(source, actor, x, y, time) {
let app = null; let app = null;
if (source instanceof AppDisplay.AppWellIcon) if (source instanceof AppDisplay.AppWellIcon)
app = this._appSystem.lookup_app(source.getId()); app = this._appSystem.get_app(source.getId());
else if (source.metaWindow) else if (source.metaWindow)
app = this._tracker.get_window_app(source.metaWindow); app = this._tracker.get_window_app(source.metaWindow);
// Don't allow favoriting of transient apps // Don't allow favoriting of transient apps
if (app == null || app.is_window_backed()) if (app == null || app.is_transient())
return DND.DragMotionResult.NO_DROP; return DND.DragMotionResult.NO_DROP;
let favorites = AppFavorites.getAppFavorites().getFavorites(); let favorites = AppFavorites.getAppFavorites().getFavorites();
@ -684,8 +686,6 @@ Dash.prototype = {
} }
this._dragPlaceholder = new DragPlaceholderItem(); 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._box.insert_actor(this._dragPlaceholder.actor,
this._dragPlaceholderPos); this._dragPlaceholderPos);
if (fadeIn) if (fadeIn)
@ -704,13 +704,13 @@ Dash.prototype = {
acceptDrop : function(source, actor, x, y, time) { acceptDrop : function(source, actor, x, y, time) {
let app = null; let app = null;
if (source instanceof AppDisplay.AppWellIcon) { if (source instanceof AppDisplay.AppWellIcon) {
app = this._appSystem.lookup_app(source.getId()); app = this._appSystem.get_app(source.getId());
} else if (source.metaWindow) { } else if (source.metaWindow) {
app = this._tracker.get_window_app(source.metaWindow); app = this._tracker.get_window_app(source.metaWindow);
} }
// Don't allow favoriting of transient apps // Don't allow favoriting of transient apps
if (app == null || app.is_window_backed()) { if (app == null || app.is_transient()) {
return false; return false;
} }

View File

@ -9,13 +9,11 @@ const Clutter = imports.gi.Clutter;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const St = imports.gi.St; const St = imports.gi.St;
const Params = imports.misc.params;
const Util = imports.misc.util; const Util = imports.misc.util;
const Main = imports.ui.main; const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const Calendar = imports.ui.calendar; const Calendar = imports.ui.calendar;
const UPowerGlib = imports.gi.UPowerGlib;
// in org.gnome.desktop.interface // in org.gnome.desktop.interface
const CLOCK_FORMAT_KEY = 'clock-format'; const CLOCK_FORMAT_KEY = 'clock-format';
@ -41,28 +39,28 @@ function _onVertSepRepaint (area)
}; };
function DateMenuButton() { function DateMenuButton() {
this._init.apply(this, arguments); this._init();
} }
DateMenuButton.prototype = { DateMenuButton.prototype = {
__proto__: PanelMenu.Button.prototype, __proto__: PanelMenu.Button.prototype,
_init: function(params) { _init: function() {
params = Params.parse(params, { showEvents: true });
let item; let item;
let hbox; let hbox;
let vbox; let vbox;
this._eventSource = new Calendar.DBusEventSource();
let menuAlignment = 0.25; let menuAlignment = 0.25;
if (St.Widget.get_default_direction() == St.TextDirection.RTL) if (St.Widget.get_default_direction() == St.TextDirection.RTL)
menuAlignment = 1.0 - menuAlignment; menuAlignment = 1.0 - menuAlignment;
PanelMenu.Button.prototype._init.call(this, menuAlignment); PanelMenu.Button.prototype._init.call(this, menuAlignment);
this._clock = new St.Label(); this._clock = new St.Label();
this.actor.add_actor(this._clock); this.actor.set_child(this._clock);
hbox = new St.BoxLayout({name: 'calendarArea' }); hbox = new St.BoxLayout({name: 'calendarArea'});
this.menu.addActor(hbox); this.menu.addActor(hbox);
// Fill up the first column // Fill up the first column
@ -75,58 +73,43 @@ DateMenuButton.prototype = {
this._date.style_class = 'datemenu-date-label'; this._date.style_class = 'datemenu-date-label';
vbox.add(this._date); vbox.add(this._date);
if (params.showEvents) { this._eventList = new Calendar.EventsList(this._eventSource);
this._eventSource = new Calendar.DBusEventSource();
this._eventList = new Calendar.EventsList(this._eventSource);
} else {
this._eventSource = null;
this._eventList = null;
}
// Calendar // Calendar
this._calendar = new Calendar.Calendar(this._eventSource); this._calendar = new Calendar.Calendar(this._eventSource);
this._calendar.connect('selected-date-changed', this._calendar.connect('selected-date-changed',
Lang.bind(this, function(calendar, date) { 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); this._eventList.setDate(date);
})); }));
vbox.add(this._calendar.actor); vbox.add(this._calendar.actor);
item = this.menu.addSettingsAction(_("Date and Time Settings"), 'gnome-datetime-panel.desktop'); item = new PopupMenu.PopupSeparatorMenuItem();
if (item) { item.setColumnWidths(1);
let separator = new PopupMenu.PopupSeparatorMenuItem(); vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
separator.setColumnWidths(1); item = new PopupMenu.PopupMenuItem(_("Date and Time Settings"));
vbox.add(separator.actor, {y_align: St.Align.END, expand: true, y_fill: false}); item.connect('activate', Lang.bind(this, this._onPreferencesActivate));
item.actor.can_focus = false;
vbox.add(item.actor);
item.actor.can_focus = false; // Add vertical separator
item.actor.reparent(vbox);
}
if (params.showEvents) { item = new St.DrawingArea({ style_class: 'calendar-vertical-separator',
// Add vertical separator pseudo_class: 'highlighted' });
item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
hbox.add(item);
item = new St.DrawingArea({ style_class: 'calendar-vertical-separator', // Fill up the second column
pseudo_class: 'highlighted' });
item.connect('repaint', Lang.bind(this, _onVertSepRepaint));
hbox.add(item);
// Fill up the second column vbox = new St.BoxLayout({vertical: true});
vbox = new St.BoxLayout({name: 'calendarEventsArea', hbox.add(vbox, { expand: true });
vertical: true});
hbox.add(vbox, { expand: true });
// Event list // Event list
vbox.add(this._eventList.actor, { expand: true }); vbox.add(this._eventList.actor, { expand: true });
item = new PopupMenu.PopupMenuItem(_("Open Calendar")); item = new PopupMenu.PopupMenuItem(_("Open Calendar"));
item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate)); item.connect('activate', Lang.bind(this, this._onOpenCalendarActivate));
item.actor.can_focus = false; item.actor.can_focus = false;
vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false}); vbox.add(item.actor, {y_align: St.Align.END, expand: true, y_fill: false});
}
// Whenever the menu is opened, select today // Whenever the menu is opened, select today
this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) { this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) {
@ -159,10 +142,6 @@ DateMenuButton.prototype = {
this._desktopSettings.connect('changed', Lang.bind(this, this._updateClockAndDate)); this._desktopSettings.connect('changed', Lang.bind(this, this._updateClockAndDate));
this._clockSettings.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 // Start the clock
this._updateClockAndDate(); this._updateClockAndDate();
}, },
@ -178,12 +157,12 @@ DateMenuButton.prototype = {
switch (format) { switch (format) {
case '24h': case '24h':
if (showDate) if (showDate)
/* Translators: This is the time format with date used /* Translators: This is the time format with date used
in 24-hour mode. */ in 24-hour mode. */
clockFormat = showSeconds ? _("%a %b %e, %R:%S") clockFormat = showSeconds ? _("%a %b %e, %R:%S")
: _("%a %b %e, %R"); : _("%a %b %e, %R");
else else
/* Translators: This is the time format without date used /* Translators: This is the time format without date used
in 24-hour mode. */ in 24-hour mode. */
clockFormat = showSeconds ? _("%a %R:%S") clockFormat = showSeconds ? _("%a %R:%S")
: _("%a %R"); : _("%a %R");
@ -191,12 +170,12 @@ DateMenuButton.prototype = {
case '12h': case '12h':
default: default:
if (showDate) if (showDate)
/* Translators: This is a time format with date used /* Translators: This is a time format with date used
for AM/PM. */ for AM/PM. */
clockFormat = showSeconds ? _("%a %b %e, %l:%M:%S %p") clockFormat = showSeconds ? _("%a %b %e, %l:%M:%S %p")
: _("%a %b %e, %l:%M %p"); : _("%a %b %e, %l:%M %p");
else else
/* Translators: This is a time format without date used /* Translators: This is a time format without date used
for AM/PM. */ for AM/PM. */
clockFormat = showSeconds ? _("%a %l:%M:%S %p") clockFormat = showSeconds ? _("%a %l:%M:%S %p")
: _("%a %l:%M %p"); : _("%a %l:%M %p");
@ -217,26 +196,16 @@ DateMenuButton.prototype = {
return false; 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() { _onOpenCalendarActivate: function() {
this.menu.close(); this.menu.close();
let calendarSettings = new Gio.Settings({ schema: 'org.gnome.desktop.default-applications.office.calendar' }); // TODO: pass the selected day
let tool = calendarSettings.get_string('exec'); Util.spawn(['evolution', '-c', 'calendar']);
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) { activateResult: function(id, params) {
params = Params.parse(params, { workspace: -1, params = Params.parse(params, { workspace: null,
timestamp: 0 }); timestamp: null });
let docInfo = this._docManager.lookupByUri(id); let docInfo = this._docManager.lookupByUri(id);
docInfo.launch(params.workspace); docInfo.launch(params.workspace ? params.workspace.index() : -1);
}, },
getInitialResultSet: function(terms) { getInitialResultSet: function(terms) {

View File

@ -22,8 +22,8 @@ const DBus = imports.dbus;
const Lang = imports.lang; const Lang = imports.lang;
const Signals = imports.signals; const Signals = imports.signals;
const AccountsService = imports.gi.AccountsService;
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Gdm = imports.gi.Gdm;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk; const Gtk = imports.gi.Gtk;
const Pango = imports.gi.Pango; const Pango = imports.gi.Pango;
@ -60,16 +60,8 @@ const logoutDialogContent = {
subjectWithUser: _("Log Out %s"), subjectWithUser: _("Log Out %s"),
subject: _("Log Out"), subject: _("Log Out"),
inhibitedDescription: _("Click Log Out to quit these applications and log out of the system."), inhibitedDescription: _("Click Log Out to quit these applications and log out of the system."),
uninhibitedDescriptionWithUser: function(user, seconds) { uninhibitedDescriptionWithUser: _("%s will be logged out automatically in %d seconds."),
return ngettext("%s will be logged out automatically in %d second.", uninhibitedDescription: _("You will be logged out automatically in %d seconds."),
"%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."), endDescription: _("Logging out of the system."),
confirmButtons: [{ signal: 'ConfirmedLogout', confirmButtons: [{ signal: 'ConfirmedLogout',
label: _("Log Out") }], label: _("Log Out") }],
@ -79,11 +71,7 @@ const logoutDialogContent = {
const shutdownDialogContent = { const shutdownDialogContent = {
subject: _("Power Off"), subject: _("Power Off"),
inhibitedDescription: _("Click Power Off to quit these applications and power off the system."), inhibitedDescription: _("Click Power Off to quit these applications and power off the system."),
uninhibitedDescription: function(seconds) { uninhibitedDescription: _("The system will power off automatically in %d 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."), endDescription: _("Powering off the system."),
confirmButtons: [{ signal: 'ConfirmedReboot', confirmButtons: [{ signal: 'ConfirmedReboot',
label: _("Restart") }, label: _("Restart") },
@ -96,11 +84,7 @@ const shutdownDialogContent = {
const restartDialogContent = { const restartDialogContent = {
subject: _("Restart"), subject: _("Restart"),
inhibitedDescription: _("Click Restart to quit these applications and restart the system."), inhibitedDescription: _("Click Restart to quit these applications and restart the system."),
uninhibitedDescription: function(seconds) { uninhibitedDescription: _("The system will restart automatically in %d 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."), endDescription: _("Restarting the system."),
confirmButtons: [{ signal: 'ConfirmedReboot', confirmButtons: [{ signal: 'ConfirmedReboot',
label: _("Restart") }], label: _("Restart") }],
@ -129,7 +113,7 @@ function findAppFromInhibitor(inhibitor) {
let app = null; let app = null;
for (let i = 0; i < candidateDesktopFiles.length; i++) { for (let i = 0; i < candidateDesktopFiles.length; i++) {
try { try {
app = appSystem.lookup_app(candidateDesktopFiles[i]); app = appSystem.get_app(candidateDesktopFiles[i]);
if (app) if (app)
break; break;
@ -189,7 +173,7 @@ ListItem.prototype = {
_onClicked: function() { _onClicked: function() {
this.emit('activate'); this.emit('activate');
this._app.activate(); this._app.activate(-1);
} }
}; };
Signals.addSignalMethods(ListItem.prototype); Signals.addSignalMethods(ListItem.prototype);
@ -253,7 +237,7 @@ EndSessionDialog.prototype = {
_init: function() { _init: function() {
ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'end-session-dialog' }); ModalDialog.ModalDialog.prototype._init.call(this, { styleClass: 'end-session-dialog' });
this._user = AccountsService.UserManager.get_default().get_user(GLib.get_user_name()); this._user = Gdm.UserManager.ref_default().get_user(GLib.get_user_name());
this._secondsLeft = 0; this._secondsLeft = 0;
this._totalSecondsToStayOpen = 0; this._totalSecondsToStayOpen = 0;
@ -404,14 +388,14 @@ EndSessionDialog.prototype = {
subject = dialogContent.subjectWithUser.format(realName); subject = dialogContent.subjectWithUser.format(realName);
if (dialogContent.uninhibitedDescriptionWithUser) if (dialogContent.uninhibitedDescriptionWithUser)
description = dialogContent.uninhibitedDescriptionWithUser(realName, displayTime); description = dialogContent.uninhibitedDescriptionWithUser.format(realName, displayTime);
else else
description = dialogContent.uninhibitedDescription(displayTime); description = dialogContent.uninhibitedDescription.format(displayTime);
} }
} }
if (!description) if (!description)
description = dialogContent.uninhibitedDescription(displayTime); description = dialogContent.uninhibitedDescription.format(displayTime);
} else { } else {
description = dialogContent.endDescription; description = dialogContent.endDescription;
} }

View File

@ -1,31 +1,17 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ /* -*- 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 GLib = imports.gi.GLib;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const St = imports.gi.St; const St = imports.gi.St;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Soup = imports.gi.Soup;
const Config = imports.misc.config; const Config = imports.misc.config;
const FileUtils = imports.misc.fileUtils;
const ModalDialog = imports.ui.modalDialog;
const API_VERSION = 1;
const ExtensionState = { const ExtensionState = {
ENABLED: 1, ENABLED: 1,
DISABLED: 2, DISABLED: 2,
ERROR: 3, 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 = { const ExtensionType = {
@ -33,54 +19,16 @@ const ExtensionType = {
PER_USER: 2 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 // Maps uuid -> metadata object
const extensionMeta = {}; const extensionMeta = {};
// Maps uuid -> importer object (extension directory tree) // Maps uuid -> importer object (extension directory tree)
const extensions = {}; const extensions = {};
// Maps uuid -> extension state object (returned from init())
const extensionStateObjs = {};
// Arrays of uuids // Arrays of uuids
var disabledExtensions;
var enabledExtensions; var enabledExtensions;
// GFile for user extensions // GFile for user extensions
var userExtensionsDir = null; 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: * versionCheck:
* @required: an array of versions we're compatible with * @required: an array of versions we're compatible with
@ -111,156 +59,13 @@ function versionCheck(required, current) {
return false; 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) { function loadExtension(dir, enabled, type) {
let info; let info;
let uuid = dir.get_basename(); let baseErrorString = 'While loading extension from "' + dir.get_parse_name() + '": ';
let metadataFile = dir.get_child('metadata.json'); let metadataFile = dir.get_child('metadata.json');
if (!metadataFile.query_exists(null)) { if (!metadataFile.query_exists(null)) {
logExtensionError(uuid, 'Missing metadata.json'); global.logError(baseErrorString + 'Missing metadata.json');
return; return;
} }
@ -268,65 +73,61 @@ function loadExtension(dir, enabled, type) {
try { try {
metadataContents = Shell.get_file_contents_utf8_sync(metadataFile.get_path()); metadataContents = Shell.get_file_contents_utf8_sync(metadataFile.get_path());
} catch (e) { } catch (e) {
logExtensionError(uuid, 'Failed to load metadata.json: ' + e); global.logError(baseErrorString + 'Failed to load metadata.json: ' + e);
return; return;
} }
let meta; let meta;
try { try {
meta = JSON.parse(metadataContents); meta = JSON.parse(metadataContents);
} catch (e) { } catch (e) {
logExtensionError(uuid, 'Failed to parse metadata.json: ' + e); global.logError(baseErrorString + 'Failed to parse metadata.json: ' + e);
return; return;
} }
let requiredProperties = ['uuid', 'name', 'description', 'shell-version']; let requiredProperties = ['uuid', 'name', 'description', 'shell-version'];
for (let i = 0; i < requiredProperties.length; i++) { for (let i = 0; i < requiredProperties.length; i++) {
let prop = requiredProperties[i]; let prop = requiredProperties[i];
if (!meta[prop]) { if (!meta[prop]) {
logExtensionError(uuid, 'missing "' + prop + '" property in metadata.json'); global.logError(baseErrorString + 'missing "' + prop + '" property in metadata.json');
return; return;
} }
} }
if (extensions[uuid] != undefined) { if (extensions[meta.uuid] != undefined) {
logExtensionError(uuid, "extension already loaded"); global.logError(baseErrorString + "extension already loaded");
return; return;
} }
// Encourage people to add this // Encourage people to add this
if (!meta['url']) { if (!meta['url']) {
global.log('Warning: Missing "url" property in metadata.json'); global.log(baseErrorString + 'Warning: Missing "url" property in metadata.json');
} }
if (uuid != meta.uuid) { let base = dir.get_basename();
logExtensionError(uuid, 'uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + uuid + '"'); if (base != meta.uuid) {
global.logError(baseErrorString + 'uuid "' + meta.uuid + '" from metadata.json does not match directory name "' + base + '"');
return; return;
} }
if (!versionCheck(meta['shell-version'], Config.PACKAGE_VERSION) || if (!versionCheck(meta['shell-version'], Config.PACKAGE_VERSION) ||
(meta['js-version'] && !versionCheck(meta['js-version'], Config.GJS_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'); global.logError(baseErrorString + 'extension is not compatible with current GNOME Shell and/or GJS version');
return; return;
} }
extensionMeta[uuid] = meta; extensionMeta[meta.uuid] = meta;
meta.type = type; extensionMeta[meta.uuid].type = type;
meta.path = dir.get_path(); extensionMeta[meta.uuid].path = dir.get_path();
meta.error = ''; if (!enabled) {
extensionMeta[meta.uuid].state = ExtensionState.DISABLED;
return;
}
// Default to error, we set success as the last step // Default to error, we set success as the last step
meta.state = ExtensionState.ERROR; extensionMeta[meta.uuid].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'); let extensionJs = dir.get_child('extension.js');
if (!extensionJs.query_exists(null)) { if (!extensionJs.query_exists(null)) {
logExtensionError(uuid, 'Missing extension.js'); global.logError(baseErrorString + 'Missing extension.js');
return; return;
} }
let stylesheetPath = null; let stylesheetPath = null;
@ -337,94 +138,48 @@ function loadExtension(dir, enabled, type) {
try { try {
theme.load_stylesheet(stylesheetFile.get_path()); theme.load_stylesheet(stylesheetFile.get_path());
} catch (e) { } catch (e) {
logExtensionError(uuid, 'Stylesheet parse error: ' + e); global.logError(baseErrorString + 'Stylesheet parse error: ' + e);
return; return;
} }
} }
let extensionModule; let extensionModule;
let extensionState = null;
try { try {
global.add_extension_importer('imports.ui.extensionSystem.extensions', meta.uuid, dir.get_path()); global.add_extension_importer('imports.ui.extensionSystem.extensions', meta.uuid, dir.get_path());
extensionModule = extensions[meta.uuid].extension; extensionModule = extensions[meta.uuid].extension;
} catch (e) { } catch (e) {
if (stylesheetPath != null) if (stylesheetPath != null)
theme.unload_stylesheet(stylesheetPath); theme.unload_stylesheet(stylesheetPath);
logExtensionError(uuid, e); global.logError(baseErrorString + e);
return; return;
} }
if (!extensionModule.main) {
if (!extensionModule.init) { global.logError(baseErrorString + 'missing \'main\' function');
logExtensionError(uuid, 'missing \'init\' function');
return; return;
} }
try { try {
extensionState = extensionModule.init(meta); extensionModule.main(meta);
} catch (e) { } catch (e) {
if (stylesheetPath != null) if (stylesheetPath != null)
theme.unload_stylesheet(stylesheetPath); theme.unload_stylesheet(stylesheetPath);
logExtensionError(uuid, 'Failed to evaluate init function:' + e); global.logError(baseErrorString + 'Failed to evaluate main function:' + e);
return; 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); 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() { function init() {
let userExtensionsPath = GLib.build_filenamev([global.userdatadir, 'extensions']); let userExtensionsPath = GLib.build_filenamev([global.userdatadir, 'extensions']);
userExtensionsDir = Gio.file_new_for_path(userExtensionsPath); userExtensionsDir = Gio.file_new_for_path(userExtensionsPath);
try { try {
if (!userExtensionsDir.query_exists(null)) userExtensionsDir.make_directory_with_parents(null);
userExtensionsDir.make_directory_with_parents(null);
} catch (e) { } catch (e) {
global.logError('' + e); global.logError('' + e);
} }
global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged); disabledExtensions = global.settings.get_strv('disabled-extensions', -1);
enabledExtensions = global.settings.get_strv(ENABLED_EXTENSIONS_KEY); enabledExtensions = global.settings.get_strv('enabled-extensions', -1);
} }
function _loadExtensionsIn(dir, type) { function _loadExtensionsIn(dir, type) {
@ -442,8 +197,11 @@ function _loadExtensionsIn(dir, type) {
if (fileType != Gio.FileType.DIRECTORY) if (fileType != Gio.FileType.DIRECTORY)
continue; continue;
let name = info.get_name(); 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 child = dir.get_child(name);
let enabled = enabledExtensions.indexOf(name) != -1;
loadExtension(child, enabled, type); loadExtension(child, enabled, type);
} }
fileEnum.close(null); fileEnum.close(null);
@ -459,73 +217,3 @@ function loadExtensions() {
_loadExtensionsIn(dir, ExtensionType.SYSTEM); _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());
}
};

View File

@ -7,21 +7,23 @@ const Gdk = imports.gi.Gdk;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Lang = imports.lang; const Lang = imports.lang;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const St = imports.gi.St; const St = imports.gi.St;
const BoxPointer = imports.ui.boxpointer; const BoxPointer = imports.ui.boxpointer;
const Main = imports.ui.main; const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray; const MessageTray = imports.ui.messageTray;
const PopupMenu = imports.ui.popupMenu;
const Tweener = imports.ui.tweener;
const KEYBOARD_SCHEMA = 'org.gnome.shell.keyboard'; const KEYBOARD_SCHEMA = 'org.gnome.shell.keyboard';
const SHOW_KEYBOARD = 'show-keyboard';
const KEYBOARD_TYPE = 'keyboard-type'; const KEYBOARD_TYPE = 'keyboard-type';
const ENABLE_DRAGGABLE = 'enable-drag';
const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications'; const ENABLE_FLOAT = 'enable-float';
const SHOW_KEYBOARD = 'screen-keyboard-enabled';
// Key constants taken from Antler // Key constants taken from Antler
// FIXME: ought to be moved into libcaribou
const PRETTY_KEYS = { const PRETTY_KEYS = {
'BackSpace': '\u232b', 'BackSpace': '\u232b',
'space': ' ', 'space': ' ',
@ -42,11 +44,11 @@ const PRETTY_KEYS = {
const CaribouKeyboardIface = { const CaribouKeyboardIface = {
name: 'org.gnome.Caribou.Keyboard', name: 'org.gnome.Caribou.Keyboard',
methods: [ { name: 'Show', methods: [ { name: 'Show',
inSignature: 'u', inSignature: '',
outSignature: '' outSignature: ''
}, },
{ name: 'Hide', { name: 'Hide',
inSignature: 'u', inSignature: '',
outSignature: '' outSignature: ''
}, },
{ name: 'SetCursorLocation', { name: 'SetCursorLocation',
@ -67,13 +69,16 @@ function Key() {
} }
Key.prototype = { Key.prototype = {
_init : function(key) { _init : function(key, key_width, key_height) {
this._key = key; this._key = key;
this.actor = this._makeKey(); this._width = key_width;
this._height = key_height;
this.actor = this._getKey();
this._extended_keys = this._key.get_extended_keys(); this._extended_keys = this._key.get_extended_keys();
this._extended_keyboard = null; this._extended_keyboard = {};
if (this._key.name == "Control_L" || this._key.name == "Alt_L") if (this._key.name == "Control_L" || this._key.name == "Alt_L")
this._key.latch = true; this._key.latch = true;
@ -96,11 +101,12 @@ Key.prototype = {
this._getExtendedKeys(); this._getExtendedKeys();
this.actor._extended_keys = this._extended_keyboard; this.actor._extended_keys = this._extended_keyboard;
this._boxPointer.actor.hide(); this._boxPointer.actor.hide();
Main.layoutManager.addChrome(this._boxPointer.actor, { visibleInFullscreen: true }); Main.chrome.addActor(this._boxPointer.actor, { visibleInFullscreen: true,
affectsStruts: false });
} }
}, },
_makeKey: function () { _getKey: function () {
let label = this._key.name; let label = this._key.name;
if (label.length > 1) { if (label.length > 1) {
@ -112,10 +118,12 @@ Key.prototype = {
} }
label = GLib.markup_escape_text(label, -1); label = GLib.markup_escape_text(label, -1);
let button = new St.Button ({ label: label, let button = new St.Button ({ label: label, style_class: 'keyboard-key' });
style_class: 'keyboard-key' });
button.width = this._width;
button.key_width = this._key.width; button.key_width = this._key.width;
button.height = this._height;
button.draggable = false;
button.connect('button-press-event', Lang.bind(this, function () { this._key.press(); })); button.connect('button-press-event', Lang.bind(this, function () { this._key.press(); }));
button.connect('button-release-event', Lang.bind(this, function () { this._key.release(); })); button.connect('button-release-event', Lang.bind(this, function () { this._key.release(); }));
@ -140,6 +148,9 @@ Key.prototype = {
let label = this._getUnichar(extended_key); let label = this._getUnichar(extended_key);
let key = new St.Button({ label: label, style_class: 'keyboard-key' }); let key = new St.Button({ label: label, style_class: 'keyboard-key' });
key.extended_key = extended_key; key.extended_key = extended_key;
key.width = this._width;
key.height = this._height;
key.draggable = false;
key.connect('button-press-event', Lang.bind(this, function () { extended_key.press(); })); key.connect('button-press-event', Lang.bind(this, function () { extended_key.press(); }));
key.connect('button-release-event', Lang.bind(this, function () { extended_key.release(); })); key.connect('button-release-event', Lang.bind(this, function () { extended_key.release(); }));
this._extended_keyboard.add(key); this._extended_keyboard.add(key);
@ -149,16 +160,16 @@ Key.prototype = {
_onEventCapture: function (actor, event) { _onEventCapture: function (actor, event) {
let source = event.get_source(); let source = event.get_source();
let type = event.type(); if (event.type() == Clutter.EventType.BUTTON_PRESS ||
(event.type() == Clutter.EventType.BUTTON_RELEASE && source.draggable)) {
if ((type == Clutter.EventType.BUTTON_PRESS || if (this._extended_keyboard.contains(source)) {
type == Clutter.EventType.BUTTON_RELEASE) && if (source.draggable) {
this._extended_keyboard.contains(source)) { source.extended_key.press();
source.extended_key.press(); source.extended_key.release();
source.extended_key.release(); }
return false; this._ungrab();
} return false;
if (type == Clutter.EventType.BUTTON_PRESS) { }
this._boxPointer.actor.hide(); this._boxPointer.actor.hide();
this._ungrab(); this._ungrab();
return true; return true;
@ -177,7 +188,7 @@ Key.prototype = {
if (this._key.show_subkeys) { if (this._key.show_subkeys) {
this.actor.fake_release(); this.actor.fake_release();
this._boxPointer.actor.raise_top(); this._boxPointer.actor.raise_top();
this._boxPointer.setPosition(this.actor, 0.5); this._boxPointer.setPosition(this.actor, 5, 0.5);
this._boxPointer.show(true); this._boxPointer.show(true);
this.actor.set_hover(false); this.actor.set_hover(false);
if (!this._grabbed) { if (!this._grabbed) {
@ -201,55 +212,30 @@ function Keyboard() {
Keyboard.prototype = { Keyboard.prototype = {
_init: function () { _init: function () {
DBus.session.exportObject('/org/gnome/Caribou/Keyboard', this); DBus.session.exportObject('/org/gnome/Caribou/Keyboard', this);
DBus.session.acquire_name('org.gnome.Caribou.Keyboard', 0, null, null);
this.actor = null; this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true, reactive: true });
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 = new Gio.Settings({ schema: KEYBOARD_SCHEMA });
this._keyboardSettings.connect('changed', Lang.bind(this, this._settingsChanged)); this._keyboardSettings.connect('changed', Lang.bind(this, this._display));
this._a11yApplicationsSettings = new Gio.Settings({ schema: A11Y_APPLICATIONS_SCHEMA });
this._a11yApplicationsSettings.connect('changed', Lang.bind(this, this._settingsChanged)); this._setupKeyboard();
this._settingsChanged();
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._redraw));
Main.layoutManager.bottomBox.add_actor(this.actor);
}, },
init: function () { init: function () {
this._redraw(); this._display();
},
_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() { _setupKeyboard: function() {
this.actor = new St.BoxLayout({ name: 'keyboard', vertical: true, reactive: true }); if (this._keyboardNotifyId)
Main.layoutManager.keyboardBox.add_actor(this.actor); this._keyboard.disconnect(this._keyboardNotifyId);
Main.layoutManager.trackChrome(this.actor); let children = this.actor.get_children();
for (let i = 0; i < children.length; i++)
children[i].destroy();
this._keyboard = new Caribou.KeyboardModel({ keyboard_type: this._keyboardSettings.get_string(KEYBOARD_TYPE) }); this._keyboard = new Caribou.KeyboardModel({ keyboard_type: this._keyboardSettings.get_string(KEYBOARD_TYPE) });
this._groups = {}; this._groups = {};
@ -259,24 +245,76 @@ Keyboard.prototype = {
this._numOfHorizKeys = 0; this._numOfHorizKeys = 0;
this._numOfVertKeys = 0; this._numOfVertKeys = 0;
this._floatId = 0;
this._addKeys(); this._addKeys();
this._keyboardNotifyId = this._keyboard.connect('notify::active-group', Lang.bind(this, this._onGroupChanged)); 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 () { _display: function () {
let focus = global.stage.key_focus; if (this._keyboard.keyboard_type != this._keyboardSettings.get_string(KEYBOARD_TYPE))
this._setupKeyboard();
// Showing an extended key popup will grab focus, but ignore that this._showKeyboard = this._keyboardSettings.get_boolean(SHOW_KEYBOARD);
if (focus && focus._extended_keys) this._draggable = this._keyboardSettings.get_boolean(ENABLE_DRAGGABLE);
return; this._floating = this._keyboardSettings.get_boolean(ENABLE_FLOAT);
if (this._floating) {
if (focus instanceof Clutter.Text) this._floatId = this.actor.connect('button-press-event', Lang.bind(this, this._startDragging));
this.show(); this._dragging = false;
}
else else
this.actor.disconnect(this._floatId);
if (this._showKeyboard)
this.show();
else {
this.hide(); this.hide();
this.destroySource();
}
},
_startDragging: function (actor, event) {
if (this._dragging) // don't allow two drags at the same time
return;
this._dragging = true;
this._preDragStageMode = global.stage_input_mode;
Clutter.grab_pointer(this.actor);
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
this._releaseId = this.actor.connect('button-release-event', Lang.bind(this, this._endDragging));
this._motionId = this.actor.connect('motion-event', Lang.bind(this, this._motionEvent));
[this._dragStartX, this._dragStartY] = event.get_coords();
[this._currentX, this._currentY] = this.actor.get_position();
},
_endDragging: function () {
if (this._dragging) {
this.actor.disconnect(this._releaseId);
this.actor.disconnect(this._motionId);
Clutter.ungrab_pointer();
global.set_stage_input_mode(this._preDragStageMode);
global.unset_cursor();
this._dragging = false;
}
return true;
},
_motionEvent: function(actor, event) {
let absX, absY;
[absX, absY] = event.get_coords();
global.set_cursor(Shell.Cursor.DND_IN_DRAG);
this._moveHandle(absX, absY);
return true;
},
_moveHandle: function (stageX, stageY) {
let x, y;
x = stageX - this._dragStartX + this._currentX;
y = stageY - this._dragStartY + this._currentY;
this.actor.set_position(x,y);
}, },
_addKeys: function () { _addKeys: function () {
@ -308,7 +346,7 @@ Keyboard.prototype = {
let trayButton = new St.Button ({ label: "tray", style_class: 'keyboard-key' }); let trayButton = new St.Button ({ label: "tray", style_class: 'keyboard-key' });
trayButton.key_width = 1; trayButton.key_width = 1;
trayButton.connect('button-press-event', Lang.bind(this, function () { trayButton.connect('button-press-event', Lang.bind(this, function () {
Main.messageTray.toggle(); Main.layoutManager.updateForTray();
})); }));
Main.overview.connect('showing', Lang.bind(this, function () { Main.overview.connect('showing', Lang.bind(this, function () {
@ -333,14 +371,14 @@ Keyboard.prototype = {
if (this._numOfHorizKeys == 0) if (this._numOfHorizKeys == 0)
this._numOfHorizKeys = children.length; this._numOfHorizKeys = children.length;
let key = children[j]; let key = children[j];
let button = new Key(key); let button = new Key(key, 0, 0);
if (key.align == 'right') if (key.align == 'right')
right_box.add(button.actor); right_box.add(button.actor);
else else
left_box.add(button.actor); left_box.add(button.actor);
if (key.name == "Caribou_Prefs") { if (key.name == "Caribou_Prefs") {
key.connect('key-released', Lang.bind(this, this.hide)); key.connect('key-released', Lang.bind(this, this._onPrefsClick));
// Add new key for hiding message tray // Add new key for hiding message tray
right_box.add(this._getTrayIcon()); right_box.add(this._getTrayIcon());
@ -352,6 +390,15 @@ Keyboard.prototype = {
layout.add(keyboard_row); layout.add(keyboard_row);
}, },
_manageTray: function () {
this.createSource();
},
_onPrefsClick: function () {
this.hide();
this._manageTray();
},
_loadRows : function (level, layout) { _loadRows : function (level, layout) {
let rows = level.get_rows(); let rows = level.get_rows();
for (let i = 0; i < rows.length; ++i) { for (let i = 0; i < rows.length; ++i) {
@ -364,9 +411,6 @@ Keyboard.prototype = {
}, },
_redraw: function () { _redraw: function () {
if (!this._enableKeyboard)
return;
let monitor = Main.layoutManager.bottomMonitor; let monitor = Main.layoutManager.bottomMonitor;
let maxHeight = monitor.height / 3; let maxHeight = monitor.height / 3;
this.actor.width = monitor.width; this.actor.width = monitor.width;
@ -396,12 +440,14 @@ Keyboard.prototype = {
let child = keys[k]; let child = keys[k];
child.width = keySize * child.key_width; child.width = keySize * child.key_width;
child.height = keySize; child.height = keySize;
child.draggable = this._draggable;
if (child._extended_keys) { if (child._extended_keys) {
let extended_keys = child._extended_keys.get_children(); let extended_keys = child._extended_keys.get_children();
for (let n = 0; n < extended_keys.length; ++n) { for (let n = 0; n < extended_keys.length; ++n) {
let extended_key = extended_keys[n]; let extended_key = extended_keys[n];
extended_key.width = keySize; extended_key.width = keySize;
extended_key.height = keySize; extended_key.height = keySize;
extended_key.draggable = this._draggable;
} }
} }
} }
@ -433,15 +479,14 @@ Keyboard.prototype = {
this._current_page.show(); this._current_page.show();
}, },
_createSource: function () { createSource: function () {
if (this._source == null) { if (this._source == null) {
this._source = new KeyboardSource(this); this._source = new KeyboardSource(this);
this._source.setTransient(true);
Main.messageTray.add(this._source); Main.messageTray.add(this._source);
} }
}, },
_destroySource: function () { destroySource: function () {
if (this._source) { if (this._source) {
this._source.destroy(); this._source.destroy();
this._source = null; this._source = null;
@ -450,53 +495,73 @@ Keyboard.prototype = {
show: function () { show: function () {
this._redraw(); this._redraw();
Main.layoutManager.showKeyboard(); Main.layoutManager.showKeyboard();
this._destroySource();
}, },
hide: function () { hide: function () {
Main.layoutManager.hideKeyboard(); Main.layoutManager.hideKeyboard();
this._createSource(); },
// Window placement method
_updatePosition: function (x, y) {
let primary = Main.layoutManager.primaryMonitor;
x -= this.actor.width / 2;
// Determines bottom/top centered
if (y <= primary.height / 2)
y += this.actor.height / 2;
else
y -= 3 * this.actor.height / 2;
// Accounting for monitor boundaries
if (x < primary.x)
x = primary.x;
if (x + this.actor.width > primary.width)
x = primary.width - this.actor.width;
this.actor.set_position(x, y);
}, },
_moveTemporarily: function () { _moveTemporarily: function () {
let currentWindow = global.screen.get_display().focus_window; this._currentWindow = global.screen.get_display().focus_window;
let rect = currentWindow.get_outer_rect(); let rect = this._currentWindow.get_outer_rect();
this._currentWindow.x = rect.x;
this._currentWindow.y = rect.y;
let newX = rect.x; let newX = this._currentWindow.x;
let newY = 3 * this.actor.height / 2; let newY = 3 * this.actor.height / 2;
currentWindow.move_frame(true, newX, newY); this._currentWindow.move_frame(true, newX, newY);
}, },
_setLocation: function (x, y) { _setLocation: function (x, y) {
if (y >= 2 * this.actor.height) if (this._floating)
this._moveTemporarily(); this._updatePosition(x, y);
else {
if (y >= 2 * this.actor.height)
this._moveTemporarily();
}
}, },
// D-Bus methods // D-Bus methods
Show: function(timestamp) { Show: function() {
if (timestamp - this._timestamp < 0) this.destroySource();
return;
this._timestamp = timestamp;
this.show(); this.show();
}, },
Hide: function(timestamp) { Hide: function() {
if (timestamp - this._timestamp < 0) if (this._currentWindow) {
return; this._currentWindow.move_frame(true, this._currentWindow.x, this._currentWindow.y);
this._currentWindow = null;
this._timestamp = timestamp; }
this.hide(); this.hide();
this._manageTray();
}, },
SetCursorLocation: function(x, y, w, h) { SetCursorLocation: function(x, y, w, h) {
// this._setLocation(x, y); this._setLocation(x, y);
}, },
SetEntryLocation: function(x, y, w, h) { SetEntryLocation: function(x, y, w, h) {
// this._setLocation(x, y); this._setLocation(x, y);
}, },
get Name() { get Name() {
@ -530,11 +595,15 @@ KeyboardSource.prototype = {
if (event.type() != Clutter.EventType.BUTTON_RELEASE) if (event.type() != Clutter.EventType.BUTTON_RELEASE)
return false; return false;
if (event.get_button() != 1)
return false;
this.open(); this.open();
return true; return true;
}, },
open: function() { open: function() {
this._keyboard.show(); this._keyboard.show();
this._keyboard.destroySource();
} }
}; };

View File

@ -2,20 +2,22 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell;
const Signals = imports.signals; const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
const Main = imports.ui.main; const Main = imports.ui.main;
const Params = imports.misc.params; const Meta = imports.gi.Meta;
const ScreenSaver = imports.misc.screenSaver; const Panel = imports.ui.panel;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const State = {
HIDDEN: 0,
SHOWING: 1,
SHOWN: 2,
HIDING: 3
};
const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5; const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
const STARTUP_ANIMATION_TIME = 0.2;
const KEYBOARD_ANIMATION_TIME = 0.5;
function LayoutManager() { function LayoutManager() {
this._init.apply(this, arguments); this._init.apply(this, arguments);
@ -28,41 +30,103 @@ LayoutManager.prototype = {
this.primaryMonitor = null; this.primaryMonitor = null;
this.primaryIndex = -1; this.primaryIndex = -1;
this._hotCorners = []; this._hotCorners = [];
this._leftPanelBarrier = 0; this.bottomBox = new Clutter.Group();
this._rightPanelBarrier = 0; this.topBox = new Clutter.Group({ clip_to_allocation: true });
this._trayBarrier = 0; this.bottomBox.add_actor(this.topBox);
this._chrome = new Chrome(this); global.screen.connect('monitors-changed', Lang.bind(this, this._monitorsChanged));
this._updateMonitors();
this.panelBox = new St.BoxLayout({ name: 'panelBox', Main.connect('layout-initialized', Lang.bind(this, this._initChrome));
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' }); Main.connect('main-ui-initialized', Lang.bind(this, this._finishInit));
this.addChrome(this.trayBox, { visibleInFullscreen: true }); },
this.trayBox.connect('allocation-changed',
Lang.bind(this, this._updateTrayBarrier));
this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox', _initChrome: function() {
reactive: true, Main.chrome.addActor(this.bottomBox, { affectsStruts: false,
track_hover: true }); visibleInFullscreen: true });
this.addChrome(this.keyboardBox, { visibleInFullscreen: true }); },
this._keyboardHeightNotifyId = 0;
global.screen.connect('monitors-changed', // _updateHotCorners needs access to Main.panel
Lang.bind(this, this._monitorsChanged)); _finishInit: function() {
this._monitorsChanged(); this._updateHotCorners();
this.topBox.height = Main.messageTray.actor.height;
this.topBox.y = - Main.messageTray.actor.height;
this._keyboardState = Main.keyboard.actor.visible ? State.SHOWN : State.HIDDEN;
this._traySummoned = true;
Main.keyboard.actor.connect('allocation-changed', Lang.bind(this, this._reposition));
},
_reposition: function () {
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function () { this._updateForKeyboard(); }));
},
_updateForKeyboard: function () {
if (Tweener.isTweening(this.bottomBox))
return;
this.topBox.y = - Main.messageTray.actor.height;
let bottom = this.bottomMonitor.y + this.bottomMonitor.height;
if (this._keyboardState == State.SHOWN)
this.bottomBox.y = bottom - Main.keyboard.actor.height;
else {
this.bottomBox.y = bottom;
this._keyboardState = State.HIDDEN;
}
},
updateForTray: function () {
if (this._keyboardState == State.SHOWN) {
if (this._traySummoned) {
Main.messageTray.lock();
this._traySummoned = false;
}
else {
Main.messageTray.unlock();
this._traySummoned = true;
}
}
else {
Main.messageTray.unlock();
this._traySummoned = false;
}
},
showKeyboard: function () {
let bottom = this.bottomMonitor.y + this.bottomMonitor.height;
// Keyboard height cannot be found directly since the first
// call to this method may be when Keyboard.Keyboard() has
// not returned; therefore the keyboard would be null
Tweener.addTween(this.bottomBox,
{ y: bottom - Main.keyboard.actor.height,
time: 0.5,
transition: 'easeOutQuad',
});
this._keyboardState = State.SHOWN;
this.updateForTray();
},
hideKeyboard: function () {
let bottom = this.bottomMonitor.y + this.bottomMonitor.height;
Tweener.addTween(this.bottomBox,
{ y: bottom,
time: 0.5,
transition: 'easeOutQuad'
});
this._keyboardState = State.HIDDEN;
this.updateForTray();
}, },
// This is called by Main after everything else is constructed; // This is called by Main after everything else is constructed;
// Chrome.init() needs access to Main.overview, which didn't exist // _updateHotCorners needs access to Main.panel, which didn't exist
// yet when the LayoutManager was constructed. // yet when the LayoutManager was constructed.
init: function() { init: function() {
this._chrome.init(); global.screen.connect('monitors-changed', Lang.bind(this, this._monitorsChanged));
this._updateHotCorners();
this._startupAnimation();
}, },
_updateMonitors: function() { _updateMonitors: function() {
@ -89,6 +153,9 @@ LayoutManager.prototype = {
} }
this.primaryMonitor = this.monitors[this.primaryIndex]; this.primaryMonitor = this.monitors[this.primaryIndex];
this.bottomMonitor = this.monitors[this.bottomIndex]; this.bottomMonitor = this.monitors[this.bottomIndex];
this.bottomBox.set_position(0, this.bottomMonitor.y + this.bottomMonitor.height);
this.bottomBox.width = this.bottomMonitor.width;
}, },
_updateHotCorners: function() { _updateHotCorners: function() {
@ -141,71 +208,12 @@ LayoutManager.prototype = {
let corner = new HotCorner(); let corner = new HotCorner();
this._hotCorners.push(corner); this._hotCorners.push(corner);
corner.actor.set_position(cornerX, cornerY); corner.actor.set_position(cornerX, cornerY);
this._chrome.addActor(corner.actor); Main.chrome.addActor(corner.actor, { affectsStruts: false });
}
},
_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() { _monitorsChanged: function() {
this._updateMonitors(); this._updateMonitors();
this._updateBoxes();
this._updateHotCorners(); this._updateHotCorners();
this.emit('monitors-changed'); this.emit('monitors-changed');
@ -226,7 +234,9 @@ LayoutManager.prototype = {
}, },
get focusIndex() { get focusIndex() {
let focusWindow = global.display.focus_window; let screen = global.screen;
let display = screen.get_display();
let focusWindow = display.focus_window;
if (focusWindow) { if (focusWindow) {
let wrect = focusWindow.get_outer_rect(); let wrect = focusWindow.get_outer_rect();
@ -245,130 +255,6 @@ LayoutManager.prototype = {
get focusMonitor() { get focusMonitor() {
return this.monitors[this.focusIndex]; 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); Signals.addSignalMethods(LayoutManager.prototype);
@ -431,22 +317,13 @@ HotCorner.prototype = {
Lang.bind(this, this._onCornerClicked)); Lang.bind(this, this._onCornerClicked));
this._corner.connect('leave-event', this._corner.connect('leave-event',
Lang.bind(this, this._onCornerLeft)); 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() { destroy: function() {
this.actor.destroy(); this.actor.destroy();
}, },
_animRipple : function(ripple, delay, time, startScale, startOpacity, finalScale) { _addRipple : function(delay, time, startScale, startOpacity, finalScale, finalOpacity) {
// We draw a ripple by using a source image and animating it scaling // We draw a ripple by using a source image and animating it scaling
// outwards and fading away. We want the ripples to move linearly // outwards and fading away. We want the ripples to move linearly
// or it looks unrealistic, but if the opacity of the ripple goes // or it looks unrealistic, but if the opacity of the ripple goes
@ -454,27 +331,25 @@ HotCorner.prototype = {
// 'onUpdate' to give a non-linear curve to the fade-away and make // 'onUpdate' to give a non-linear curve to the fade-away and make
// it more visible in the middle section. // it more visible in the middle section.
ripple._opacity = startOpacity; 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;
if (ripple.get_direction() == St.TextDirection.RTL) if (ripple.get_direction() == St.TextDirection.RTL)
ripple.set_anchor_point_from_gravity(Clutter.Gravity.NORTH_EAST); 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_x: finalScale,
scale_y: finalScale, scale_y: finalScale,
delay: delay, delay: delay,
time: time, time: time,
transition: 'linear', transition: 'linear',
onUpdate: function() { ripple.opacity = 255 * Math.sqrt(ripple._opacity); }, onUpdate: function() { ripple.opacity = 255 * Math.sqrt(ripple._opacity); },
onComplete: function() { ripple.visible = false; } }); onComplete: function() { ripple.destroy(); } });
Main.uiGroup.add_actor(ripple);
}, },
rippleAnimation: function() { rippleAnimation: function() {
@ -482,10 +357,10 @@ HotCorner.prototype = {
// parameters were found by trial and error, so don't look // parameters were found by trial and error, so don't look
// for them to make perfect sense mathematically // for them to make perfect sense mathematically
// delay time scale opacity => scale // delay time scale opacity => scale opacity
this._animRipple(this._ripple1, 0.0, 0.83, 0.25, 1.0, 1.5); this._addRipple(0.0, 0.83, 0.25, 1.0, 1.5, 0.0);
this._animRipple(this._ripple2, 0.05, 1.0, 0.0, 0.7, 1.25); this._addRipple(0.05, 1.0, 0.0, 0.7, 1.25, 0.0);
this._animRipple(this._ripple3, 0.35, 1.0, 0.0, 0.3, 1); this._addRipple(0.35, 1.0, 0.0, 0.3, 1, 0.0);
}, },
handleDragOver: function(source, actor, x, y, time) { handleDragOver: function(source, actor, x, y, time) {
@ -545,423 +420,3 @@ HotCorner.prototype = {
return false; 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,7 +6,6 @@ const GConf = imports.gi.GConf;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk; const Gtk = imports.gi.Gtk;
const Meta = imports.gi.Meta;
const Pango = imports.gi.Pango; const Pango = imports.gi.Pango;
const St = imports.gi.St; const St = imports.gi.St;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
@ -223,9 +222,10 @@ function WindowList() {
WindowList.prototype = { WindowList.prototype = {
_init : function () { _init : function () {
this.actor = new St.BoxLayout({ name: 'Windows', vertical: true, style: 'spacing: 8px' }); this.actor = new St.BoxLayout({ name: 'Windows', vertical: true, style: 'spacing: 8px' });
let display = global.screen.get_display();
let tracker = Shell.WindowTracker.get_default(); let tracker = Shell.WindowTracker.get_default();
this._updateId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._updateWindowList)); this._updateId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._updateWindowList));
global.display.connect('window-created', Lang.bind(this, this._updateWindowList)); display.connect('window-created', Lang.bind(this, this._updateWindowList));
tracker.connect('tracked-windows-changed', Lang.bind(this, this._updateWindowList)); tracker.connect('tracked-windows-changed', Lang.bind(this, this._updateWindowList));
}, },
@ -248,7 +248,7 @@ WindowList.prototype = {
box.add(propsBox); box.add(propsBox);
propsBox.add(new St.Label({ text: 'wmclass: ' + metaWindow.get_wm_class() })); propsBox.add(new St.Label({ text: 'wmclass: ' + metaWindow.get_wm_class() }));
let app = tracker.get_window_app(metaWindow); let app = tracker.get_window_app(metaWindow);
if (app != null && !app.is_window_backed()) { if (app != null && !app.is_transient()) {
let icon = app.create_icon_texture(22); let icon = app.create_icon_texture(22);
let propBox = new St.BoxLayout({ style: 'spacing: 6px; ' }); let propBox = new St.BoxLayout({ style: 'spacing: 6px; ' });
propsBox.add(propBox); propsBox.add(propBox);
@ -607,9 +607,6 @@ Memory.prototype = {
this._gjs_closure = new St.Label(); this._gjs_closure = new St.Label();
this.actor.add(this._gjs_closure); 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', this._gcbutton = new St.Button({ label: 'Full GC',
style_class: 'lg-obj-inspector-button' }); style_class: 'lg-obj-inspector-button' });
this._gcbutton.connect('clicked', Lang.bind(this, function () { global.gc(); this._renderText(); })); this._gcbutton.connect('clicked', Lang.bind(this, function () { global.gc(); this._renderText(); }));
@ -629,7 +626,6 @@ Memory.prototype = {
this._gjs_gobject.text = 'gjs_gobject: ' + memInfo.gjs_gobject; this._gjs_gobject.text = 'gjs_gobject: ' + memInfo.gjs_gobject;
this._gjs_function.text = 'gjs_function: ' + memInfo.gjs_function; this._gjs_function.text = 'gjs_function: ' + memInfo.gjs_function;
this._gjs_closure.text = 'gjs_closure: ' + memInfo.gjs_closure; this._gjs_closure.text = 'gjs_closure: ' + memInfo.gjs_closure;
this._last_gc_seconds_ago.text = 'last_gc_seconds_ago: ' + memInfo.last_gc_seconds_ago;
} }
}; };
@ -643,32 +639,23 @@ Extensions.prototype = {
name: 'lookingGlassExtensions' }); name: 'lookingGlassExtensions' });
this._noExtensions = new St.Label({ style_class: 'lg-extensions-none', this._noExtensions = new St.Label({ style_class: 'lg-extensions-none',
text: _("No extensions installed") }); text: _("No extensions installed") });
this._numExtensions = 0;
this._extensionsList = new St.BoxLayout({ vertical: true, this._extensionsList = new St.BoxLayout({ vertical: true,
style_class: 'lg-extensions-list' }); style_class: 'lg-extensions-list' });
this._extensionsList.add(this._noExtensions);
this.actor.add(this._extensionsList); 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));
}, },
_loadExtension: function(o, uuid) { _loadExtensionList: function() {
let extension = ExtensionSystem.extensionMeta[uuid]; let extensions = ExtensionSystem.extensionMeta;
// There can be cases where we create dummy extension metadata let totalExtensions = 0;
// that's not really a proper extension. Don't bother with these. for (let uuid in extensions) {
if (!extension.name) let extensionDisplay = this._createExtensionDisplay(extensions[uuid]);
return; this._extensionsList.add(extensionDisplay);
totalExtensions++;
let extensionDisplay = this._createExtensionDisplay(extension); }
if (this._numExtensions == 0) if (totalExtensions == 0) {
this._extensionsList.remove_actor(this._noExtensions); this._extensionsList.add(this._noExtensions);
}
this._numExtensions ++;
this._extensionsList.add(extensionDisplay);
}, },
_onViewSource: function (actor) { _onViewSource: function (actor) {
@ -695,8 +682,6 @@ Extensions.prototype = {
return _("Error"); return _("Error");
case ExtensionSystem.ExtensionState.OUT_OF_DATE: case ExtensionSystem.ExtensionState.OUT_OF_DATE:
return _("Out of date"); return _("Out of date");
case ExtensionSystem.ExtensionState.DOWNLOADING:
return _("Downloading");
} }
return 'Unknown'; // Not translated, shouldn't appear return 'Unknown'; // Not translated, shouldn't appear
}, },
@ -707,7 +692,7 @@ Extensions.prototype = {
text: meta.name }); text: meta.name });
box.add(name, { expand: true }); box.add(name, { expand: true });
let description = new St.Label({ style_class: 'lg-extension-description', let description = new St.Label({ style_class: 'lg-extension-description',
text: meta.description || 'No description' }); text: meta.description });
box.add(description, { expand: true }); box.add(description, { expand: true });
let metaBox = new St.BoxLayout(); let metaBox = new St.BoxLayout();
@ -766,13 +751,7 @@ LookingGlass.prototype = {
Lang.bind(this, this._updateFont)); Lang.bind(this, this._updateFont));
this._updateFont(); this._updateFont();
// We want it to appear to slide out from underneath the panel Main.uiGroup.add_actor(this.actor);
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(); this._objInspector = new ObjInspector();
Main.uiGroup.add_actor(this._objInspector.actor); Main.uiGroup.add_actor(this._objInspector.actor);
@ -853,8 +832,6 @@ LookingGlass.prototype = {
this._history = new History.HistoryManager({ gsettingsKey: HISTORY_KEY, this._history = new History.HistoryManager({ gsettingsKey: HISTORY_KEY,
entry: this._entry.clutter_text }); entry: this._entry.clutter_text });
this._resize();
}, },
_updateFont: function() { _updateFont: function() {
@ -929,18 +906,13 @@ LookingGlass.prototype = {
this.open(); this.open();
}, },
_queueResize: function() { _resizeTo: function(actor) {
Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
Lang.bind(this, function () { this._resize(); }));
},
_resize: function() {
let primary = Main.layoutManager.primaryMonitor; let primary = Main.layoutManager.primaryMonitor;
let myWidth = primary.width * 0.7; let myWidth = primary.width * 0.7;
let availableHeight = primary.height - Main.layoutManager.keyboardBox.height; let myHeight = primary.height * 0.7;
let myHeight = Math.min(primary.height * 0.7, availableHeight * 0.9); let [srcX, srcY] = actor.get_transformed_position();
this.actor.x = (primary.width - myWidth) / 2; this.actor.x = srcX + (primary.width - myWidth) / 2;
this._hiddenY = this.actor.get_parent().height - myHeight - 4; // -4 to hide the top corners this._hiddenY = srcY + actor.height - myHeight - 4; // -4 to hide the top corners
this._targetY = this._hiddenY + myHeight; this._targetY = this._hiddenY + myHeight;
this.actor.y = this._hiddenY; this.actor.y = this._hiddenY;
this.actor.width = myWidth; this.actor.width = myWidth;
@ -950,6 +922,14 @@ LookingGlass.prototype = {
this._targetY + Math.floor(myHeight * 0.1)); 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) { insertObject: function(obj) {
this._pushResult('<insert>', obj); this._pushResult('<insert>', obj);
}, },
@ -982,6 +962,7 @@ LookingGlass.prototype = {
this._notebook.selectIndex(0); this._notebook.selectIndex(0);
this.actor.show(); this.actor.show();
this.actor.lower(Main.chrome.actor);
this._open = true; this._open = true;
this._history.lastItem(); this._history.lastItem();

View File

@ -10,10 +10,12 @@ const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const Meta = imports.gi.Meta; const Meta = imports.gi.Meta;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
const AutomountManager = imports.ui.automountManager; const AutomountManager = imports.ui.automountManager;
const AutorunManager = imports.ui.autorunManager; const AutorunManager = imports.ui.autorunManager;
const Chrome = imports.ui.chrome;
const CtrlAltTab = imports.ui.ctrlAltTab; const CtrlAltTab = imports.ui.ctrlAltTab;
const EndSessionDialog = imports.ui.endSessionDialog; const EndSessionDialog = imports.ui.endSessionDialog;
const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent; const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
@ -27,7 +29,6 @@ const PlaceDisplay = imports.ui.placeDisplay;
const RunDialog = imports.ui.runDialog; const RunDialog = imports.ui.runDialog;
const Layout = imports.ui.layout; const Layout = imports.ui.layout;
const LookingGlass = imports.ui.lookingGlass; const LookingGlass = imports.ui.lookingGlass;
const NetworkAgent = imports.ui.networkAgent;
const NotificationDaemon = imports.ui.notificationDaemon; const NotificationDaemon = imports.ui.notificationDaemon;
const WindowAttentionHandler = imports.ui.windowAttentionHandler; const WindowAttentionHandler = imports.ui.windowAttentionHandler;
const Scripting = imports.ui.scripting; const Scripting = imports.ui.scripting;
@ -44,113 +45,36 @@ DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
let automountManager = null; let automountManager = null;
let autorunManager = null; let autorunManager = null;
let chrome = null;
let panel = null; let panel = null;
let hotCorners = [];
let placesManager = null; let placesManager = null;
let overview = null; let overview = null;
let runDialog = null;
let lookingGlass = null;
let wm = null; let wm = null;
let messageTray = null; let messageTray = null;
let notificationDaemon = null; let notificationDaemon = null;
let windowAttentionHandler = null; let windowAttentionHandler = null;
let telepathyClient = null; let telepathyClient = null;
let ctrlAltTabManager = null; let ctrlAltTabManager = null;
let recorder = null;
let shellDBusService = null; let shellDBusService = null;
let modalCount = 0;
let modalActorFocusStack = [];
let uiGroup = null; let uiGroup = null;
let magnifier = null; let magnifier = null;
let xdndHandler = null; let xdndHandler = null;
let statusIconDispatcher = null; let statusIconDispatcher = null;
let keyboard = null; let keyboard = null;
let layoutManager = null; let layoutManager = null;
let networkAgent = null;
let _runDialog = null;
let lookingGlass = null;
let _recorder = null;
let _modalCount = 0;
let _modalActorFocusStack = [];
let _errorLogStack = []; let _errorLogStack = [];
let _startDate; let _startDate;
let _defaultCssStylesheet = null; let _defaultCssStylesheet = null;
let _cssStylesheet = null; let _cssStylesheet = null;
let _gdmCssStylesheet = null;
let background = null; const Main = this;
Signals.addSignalMethods(Main)
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() { function start() {
// Monkey patch utility functions into the global proxy; // Monkey patch utility functions into the global proxy;
@ -171,6 +95,10 @@ function start() {
// back into sync ones. // back into sync ones.
DBus.session.flush(); 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 // Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
// also initialize ShellAppSystem first. ShellAppSystem // also initialize ShellAppSystem first. ShellAppSystem
// needs to load all the .desktop files, and ShellWindowTracker // needs to load all the .desktop files, and ShellWindowTracker
@ -189,50 +117,85 @@ function start() {
global.stage.no_clear_hint = true; global.stage.no_clear_hint = true;
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css'; _defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
_gdmCssStylesheet = global.datadir + '/theme/gdm.css';
loadTheme(); 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. // Set up stage hierarchy to group all UI actors under one container.
uiGroup = new Shell.GenericContainer({ name: 'uiGroup' }); uiGroup = new Clutter.Group();
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); St.set_ui_root(global.stage, uiGroup);
global.window_group.reparent(uiGroup); global.window_group.reparent(uiGroup);
global.overlay_group.reparent(uiGroup); global.overlay_group.reparent(uiGroup);
global.stage.add_actor(uiGroup); global.stage.add_actor(uiGroup);
// Initialize JS modules. We do this in several steps, so that
// less-fundamental modules can depend on more-fundamental ones.
// Overall layout management
layoutManager = new Layout.LayoutManager(); layoutManager = new Layout.LayoutManager();
xdndHandler = new XdndHandler.XdndHandler(); chrome = new Chrome.Chrome();
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager(); ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
// This overview object is just a stub for non-user sessions Main.emit('layout-initialized');
overview = new Overview.Overview({ isDummy: global.session_type != Shell.SessionType.USER });
magnifier = new Magnifier.Magnifier(); // Major UI elements; initialize overview first since both panel
statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher(); // and messageTray connect to its signals
overview = new Overview.Overview();
panel = new Panel.Panel(); panel = new Panel.Panel();
wm = new WindowManager.WindowManager();
messageTray = new MessageTray.MessageTray(); messageTray = new MessageTray.MessageTray();
keyboard = new Keyboard.Keyboard(); keyboard = new Keyboard.Keyboard();
notificationDaemon = new NotificationDaemon.NotificationDaemon(); Main.emit('main-ui-initialized');
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
if (global.session_type == Shell.SessionType.USER) // Now the rest of the JS modules (arbitrarily in alphabetical
_createUserSession(); // order).
else if (global.session_type == Shell.SessionType.GDM)
_createGDMSession();
panel.startStatusArea();
layoutManager.init();
keyboard.init(); keyboard.init();
overview.init(); magnifier = new Magnifier.Magnifier();
notificationDaemon = new NotificationDaemon.NotificationDaemon();
placesManager = new PlaceDisplay.PlacesManager();
statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher();
telepathyClient = new TelepathyClient.Client();
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
wm = new WindowManager.WindowManager();
xdndHandler = new XdndHandler.XdndHandler();
automountManager = new AutomountManager.AutomountManager();
autorunManager = new AutorunManager.AutorunManager();
if (global.session_type == Shell.SessionType.USER) Main.emit('initialized');
_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 // Provide the bus object for gnome-session to
// initiate logouts. // initiate logouts.
@ -241,7 +204,15 @@ function start() {
// Attempt to become a PolicyKit authentication agent // Attempt to become a PolicyKit authentication agent
PolkitAuthenticationAgent.init() PolkitAuthenticationAgent.init()
_startDate = new Date(); ExtensionSystem.init();
ExtensionSystem.loadExtensions();
// Initialize the panel status area now that extensions are loaded
panel.startStatusArea();
panel.startupAnimation();
let display = global.screen.get_display();
display.connect('overlay-key', Lang.bind(overview, overview.toggle));
global.stage.connect('captured-event', _globalKeyPressHandler); global.stage.connect('captured-event', _globalKeyPressHandler);
@ -460,9 +431,6 @@ function loadTheme() {
let theme = new St.Theme ({ application_stylesheet: cssStylesheet }); let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
if (global.session_type == Shell.SessionType.GDM)
theme.load_stylesheet(_gdmCssStylesheet);
if (previousTheme) { if (previousTheme) {
let customStylesheets = previousTheme.get_custom_stylesheets(); let customStylesheets = previousTheme.get_custom_stylesheets();
@ -473,19 +441,6 @@ function loadTheme() {
themeContext.set_theme (theme); 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: * notifyError:
* @msg: An error message * @msg: An error message
@ -500,7 +455,11 @@ function notifyError(msg, details) {
else else
log("error: " + msg) log("error: " + msg)
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);
} }
/** /**
@ -573,7 +532,7 @@ function getWindowActorsForWorkspace(workspaceIndex) {
// all key events will be delivered to the stage, so ::captured-event // all key events will be delivered to the stage, so ::captured-event
// on the stage can be used for global keybindings.) // on the stage can be used for global keybindings.)
function _globalKeyPressHandler(actor, event) { function _globalKeyPressHandler(actor, event) {
if (modalCount == 0) if (_modalCount == 0)
return false; return false;
if (event.type() != Clutter.EventType.KEY_PRESS) if (event.type() != Clutter.EventType.KEY_PRESS)
return false; return false;
@ -582,8 +541,9 @@ function _globalKeyPressHandler(actor, event) {
let keyCode = event.get_key_code(); let keyCode = event.get_key_code();
let modifierState = Shell.get_event_state(event); 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 // This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
let action = global.display.get_keybinding_action(keyCode, modifierState); let action = display.get_keybinding_action(keyCode, modifierState);
// The screenshot action should always be available (even if a // The screenshot action should always be available (even if a
// modal dialog is present) // modal dialog is present)
@ -595,9 +555,9 @@ function _globalKeyPressHandler(actor, event) {
return true; return true;
} }
// Other bindings are only available to the user session when the overview is up and // Other bindings are only available when the overview is up and
// no modal dialog is present. // no modal dialog is present.
if (global.session_type == Shell.SessionType.USER && (!overview.visible || modalCount > 1)) if (!overview.visible || _modalCount > 1)
return false; return false;
// This isn't a Meta.KeyBindingAction yet // This isn't a Meta.KeyBindingAction yet
@ -606,16 +566,6 @@ function _globalKeyPressHandler(actor, event) {
return true; 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) { switch (action) {
// left/right would effectively act as synonyms for up/down if we enabled them; // 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. // but that could be considered confusing; we also disable them in the main view.
@ -639,14 +589,17 @@ function _globalKeyPressHandler(actor, event) {
case Meta.KeyBindingAction.PANEL_MAIN_MENU: case Meta.KeyBindingAction.PANEL_MAIN_MENU:
overview.hide(); overview.hide();
return true; return true;
case Meta.KeyBindingAction.SWITCH_PANELS:
ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK);
return true;
} }
return false; return false;
} }
function _findModal(actor) { function _findModal(actor) {
for (let i = 0; i < modalActorFocusStack.length; i++) { for (let i = 0; i < _modalActorFocusStack.length; i++) {
if (modalActorFocusStack[i].actor == actor) if (_modalActorFocusStack[i].actor == actor)
return i; return i;
} }
return -1; return -1;
@ -676,7 +629,7 @@ function pushModal(actor, timestamp) {
if (timestamp == undefined) if (timestamp == undefined)
timestamp = global.get_current_time(); timestamp = global.get_current_time();
if (modalCount == 0) { if (_modalCount == 0) {
if (!global.begin_modal(timestamp)) { if (!global.begin_modal(timestamp)) {
log('pushModal: invocation of begin_modal failed'); log('pushModal: invocation of begin_modal failed');
return false; return false;
@ -685,11 +638,11 @@ function pushModal(actor, timestamp) {
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN); global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
modalCount += 1; _modalCount += 1;
let actorDestroyId = actor.connect('destroy', function() { let actorDestroyId = actor.connect('destroy', function() {
let index = _findModal(actor); let index = _findModal(actor);
if (index >= 0) if (index >= 0)
modalActorFocusStack.splice(index, 1); _modalActorFocusStack.splice(index, 1);
}); });
let curFocus = global.stage.get_key_focus(); let curFocus = global.stage.get_key_focus();
let curFocusDestroyId; let curFocusDestroyId;
@ -697,10 +650,10 @@ function pushModal(actor, timestamp) {
curFocusDestroyId = curFocus.connect('destroy', function() { curFocusDestroyId = curFocus.connect('destroy', function() {
let index = _findModal(actor); let index = _findModal(actor);
if (index >= 0) if (index >= 0)
modalActorFocusStack[index].actor = null; _modalActorFocusStack[index].actor = null;
}); });
} }
modalActorFocusStack.push({ actor: actor, _modalActorFocusStack.push({ actor: actor,
focus: curFocus, focus: curFocus,
destroyId: actorDestroyId, destroyId: actorDestroyId,
focusDestroyId: curFocusDestroyId }); focusDestroyId: curFocusDestroyId });
@ -735,28 +688,28 @@ function popModal(actor, timestamp) {
throw new Error('incorrect pop'); throw new Error('incorrect pop');
} }
modalCount -= 1; _modalCount -= 1;
let record = modalActorFocusStack[focusIndex]; let record = _modalActorFocusStack[focusIndex];
record.actor.disconnect(record.destroyId); record.actor.disconnect(record.destroyId);
if (focusIndex == modalActorFocusStack.length - 1) { if (focusIndex == _modalActorFocusStack.length - 1) {
if (record.focus) if (record.focus)
record.focus.disconnect(record.focusDestroyId); record.focus.disconnect(record.focusDestroyId);
global.stage.set_key_focus(record.focus); global.stage.set_key_focus(record.focus);
} else { } else {
let t = modalActorFocusStack[modalActorFocusStack.length - 1]; let t = _modalActorFocusStack[_modalActorFocusStack.length - 1];
if (t.focus) if (t.focus)
t.focus.disconnect(t.focusDestroyId); t.focus.disconnect(t.focusDestroyId);
// Remove from the middle, shift the focus chain up // Remove from the middle, shift the focus chain up
for (let i = modalActorFocusStack.length - 1; i > focusIndex; i--) { for (let i = _modalActorFocusStack.length - 1; i > focusIndex; i--) {
modalActorFocusStack[i].focus = modalActorFocusStack[i - 1].focus; _modalActorFocusStack[i].focus = _modalActorFocusStack[i - 1].focus;
modalActorFocusStack[i].focusDestroyId = modalActorFocusStack[i - 1].focusDestroyId; _modalActorFocusStack[i].focusDestroyId = _modalActorFocusStack[i - 1].focusDestroyId;
} }
} }
modalActorFocusStack.splice(focusIndex, 1); _modalActorFocusStack.splice(focusIndex, 1);
if (modalCount > 0) if (_modalCount > 0)
return; return;
global.end_modal(timestamp); global.end_modal(timestamp);
@ -766,15 +719,16 @@ function popModal(actor, timestamp) {
function createLookingGlass() { function createLookingGlass() {
if (lookingGlass == null) { if (lookingGlass == null) {
lookingGlass = new LookingGlass.LookingGlass(); lookingGlass = new LookingGlass.LookingGlass();
lookingGlass.slaveTo(panel.actor);
} }
return lookingGlass; return lookingGlass;
} }
function getRunDialog() { function getRunDialog() {
if (runDialog == null) { if (_runDialog == null) {
runDialog = new RunDialog.RunDialog(); _runDialog = new RunDialog.RunDialog();
} }
return runDialog; return _runDialog;
} }
/** /**

View File

@ -68,19 +68,14 @@ function _fixMarkup(text, allowMarkup) {
// Support &amp;, &quot;, &apos;, &lt; and &gt;, escape all other // Support &amp;, &quot;, &apos;, &lt; and &gt;, escape all other
// occurrences of '&'. // occurrences of '&'.
let _text = text.replace(/&(?!amp;|quot;|apos;|lt;|gt;)/g, '&amp;'); let _text = text.replace(/&(?!amp;|quot;|apos;|lt;|gt;)/g, '&amp;');
// Support <b>, <i>, and <u>, escape anything else // Support <b>, <i>, and <u>, escape anything else
// so it displays as raw markup. // so it displays as raw markup.
_text = _text.replace(/<(?!\/?[biu]>)/g, '&lt;'); return _text.replace(/<(\/?[^biu]>|[^>\/][^>])/g, '&lt;$1');
} else {
try { // Escape everything
Pango.parse_markup(_text, -1, ''); let _text = text.replace(/&/g, '&amp;');
return _text; return _text.replace(/</g, '&lt;');
} catch (e) {}
} }
// !allowMarkup, or invalid markup
return GLib.markup_escape_text(text, -1);
} }
function URLHighlighter(text, lineWrap, allowMarkup) { function URLHighlighter(text, lineWrap, allowMarkup) {
@ -111,21 +106,12 @@ URLHighlighter.prototype = {
this.setMarkup(text, allowMarkup); this.setMarkup(text, allowMarkup);
this.actor.connect('button-press-event', Lang.bind(this, function(actor, event) { 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 // Keep Notification.actor from seeing this and taking
// a pointer grab, which would block our button-release-event // a pointer grab, which would block our button-release-event
// handler, if an URL is clicked // handler, if an URL is clicked
return this._findUrlAtPos(event) != -1; return this._findUrlAtPos(event) != -1;
})); }));
this.actor.connect('button-release-event', Lang.bind(this, function (actor, event) { 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); let urlId = this._findUrlAtPos(event);
if (urlId != -1) { if (urlId != -1) {
let url = this._urls[urlId].url; let url = this._urls[urlId].url;
@ -143,9 +129,6 @@ URLHighlighter.prototype = {
return false; return false;
})); }));
this.actor.connect('motion-event', Lang.bind(this, function(actor, event) { 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); let urlId = this._findUrlAtPos(event);
if (urlId != -1 && !this._cursorChanged) { if (urlId != -1 && !this._cursorChanged) {
global.set_cursor(Shell.Cursor.POINTING_HAND); global.set_cursor(Shell.Cursor.POINTING_HAND);
@ -157,9 +140,6 @@ URLHighlighter.prototype = {
return false; return false;
})); }));
this.actor.connect('leave-event', Lang.bind(this, function() { this.actor.connect('leave-event', Lang.bind(this, function() {
if (!this.actor.visible || this.actor.get_paint_opacity() == 0)
return;
if (this._cursorChanged) { if (this._cursorChanged) {
this._cursorChanged = false; this._cursorChanged = false;
global.unset_cursor(); global.unset_cursor();
@ -249,7 +229,9 @@ FocusGrabber.prototype = {
this.actor = actor; this.actor = actor;
this._prevFocusedWindow = global.display.focus_window; let metaDisplay = global.screen.get_display();
this._prevFocusedWindow = metaDisplay.focus_window;
this._prevKeyFocusActor = global.stage.get_key_focus(); this._prevKeyFocusActor = global.stage.get_key_focus();
if (!Main.overview.visible) if (!Main.overview.visible)
@ -284,8 +266,7 @@ FocusGrabber.prototype = {
let source = event.get_source(); let source = event.get_source();
switch (event.type()) { switch (event.type()) {
case Clutter.EventType.BUTTON_PRESS: 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); this.emit('button-pressed', source);
break; break;
case Clutter.EventType.KEY_PRESS: case Clutter.EventType.KEY_PRESS:
@ -304,6 +285,8 @@ FocusGrabber.prototype = {
if (!this._hasFocus) if (!this._hasFocus)
return; return;
let metaDisplay = global.screen.get_display();
if (this._focusActorChangedId > 0) { if (this._focusActorChangedId > 0) {
global.stage.disconnect(this._focusActorChangedId); global.stage.disconnect(this._focusActorChangedId);
this._focusActorChangedId = 0; this._focusActorChangedId = 0;
@ -322,8 +305,8 @@ FocusGrabber.prototype = {
this._hasFocus = false; this._hasFocus = false;
this.emit('focus-ungrabbed'); this.emit('focus-ungrabbed');
if (this._prevFocusedWindow && !global.display.focus_window) { if (this._prevFocusedWindow && !metaDisplay.focus_window) {
global.display.set_input_focus_window(this._prevFocusedWindow, false, global.get_current_time()); metaDisplay.set_input_focus_window(this._prevFocusedWindow, false, global.get_current_time());
this._prevFocusedWindow = null; this._prevFocusedWindow = null;
} }
if (this._prevKeyFocusActor) { if (this._prevKeyFocusActor) {
@ -412,11 +395,8 @@ function Notification(source, title, banner, params) {
} }
Notification.prototype = { Notification.prototype = {
IMAGE_SIZE: 125,
_init: function(source, title, banner, params) { _init: function(source, title, banner, params) {
this.source = source; this.source = source;
this.title = title;
this.urgency = Urgency.NORMAL; this.urgency = Urgency.NORMAL;
this.resident = false; this.resident = false;
// 'transient' is a reserved keyword in JS, so we have to use an alternate variable name // 'transient' is a reserved keyword in JS, so we have to use an alternate variable name
@ -431,7 +411,6 @@ Notification.prototype = {
this._titleDirection = St.TextDirection.NONE; this._titleDirection = St.TextDirection.NONE;
this._spacing = 0; this._spacing = 0;
this._scrollPolicy = Gtk.PolicyType.AUTOMATIC; this._scrollPolicy = Gtk.PolicyType.AUTOMATIC;
this._imageBin = null;
source.connect('destroy', Lang.bind(this, source.connect('destroy', Lang.bind(this,
function (source, reason) { function (source, reason) {
@ -461,19 +440,9 @@ Notification.prototype = {
this._bannerBox.connect('allocate', Lang.bind(this, this._bannerBoxAllocate)); this._bannerBox.connect('allocate', Lang.bind(this, this._bannerBoxAllocate));
this._table.add(this._bannerBox, { row: 0, this._table.add(this._bannerBox, { row: 0,
col: 1, col: 1,
col_span: 2,
x_expand: false,
y_expand: false, y_expand: false,
y_fill: 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._titleLabel = new St.Label();
this._bannerBox.add_actor(this._titleLabel); this._bannerBox.add_actor(this._titleLabel);
this._bannerUrlHighlighter = new URLHighlighter(); this._bannerUrlHighlighter = new URLHighlighter();
@ -504,11 +473,8 @@ Notification.prototype = {
let oldFocus = global.stage.key_focus; let oldFocus = global.stage.key_focus;
if (this._icon && (params.icon || params.clear)) { if (this._icon)
this._icon.destroy(); this._icon.destroy();
this._icon = null;
}
// We always clear the content area if we don't have custom // We always clear the content area if we don't have custom
// content because it might contain the @banner that didn't // content because it might contain the @banner that didn't
// fit in the banner mode. // fit in the banner mode.
@ -528,23 +494,17 @@ Notification.prototype = {
this._actionArea = null; this._actionArea = null;
this._buttonBox = null; this._buttonBox = null;
} }
if (this._imageBin && params.clear) if (!this._scrollArea && !this._actionArea)
this.unsetImage();
if (!this._scrollArea && !this._actionArea && !this._imageBin)
this._table.remove_style_class_name('multi-line-notification'); this._table.remove_style_class_name('multi-line-notification');
if (!this._icon) { this._icon = params.icon || this.source.createNotificationIcon();
this._icon = params.icon || this.source.createNotificationIcon(); this._table.add(this._icon, { row: 0,
this._table.add(this._icon, { row: 0, col: 0,
col: 0, x_expand: false,
x_expand: false, y_expand: false,
y_expand: false, y_fill: false,
y_fill: false, y_align: St.Align.START });
y_align: St.Align.START });
}
this.title = title;
title = title ? _fixMarkup(title.replace(/\n/g, ' '), params.titleMarkup) : ''; title = title ? _fixMarkup(title.replace(/\n/g, ' '), params.titleMarkup) : '';
this._titleLabel.clutter_text.set_markup('<b>' + title + '</b>'); this._titleLabel.clutter_text.set_markup('<b>' + title + '</b>');
@ -598,9 +558,7 @@ Notification.prototype = {
vscrollbar_policy: this._scrollPolicy, vscrollbar_policy: this._scrollPolicy,
hscrollbar_policy: Gtk.PolicyType.NEVER, hscrollbar_policy: Gtk.PolicyType.NEVER,
style_class: 'vfade' }); style_class: 'vfade' });
this._table.add(this._scrollArea, { row: 1, this._table.add(this._scrollArea, { row: 1, col: 1 });
col: 2 });
this._updateLastColumnSettings();
this._contentArea = new St.BoxLayout({ name: 'notification-body', this._contentArea = new St.BoxLayout({ name: 'notification-body',
vertical: true }); vertical: true });
this._scrollArea.add_actor(this._contentArea); this._scrollArea.add_actor(this._contentArea);
@ -677,52 +635,13 @@ Notification.prototype = {
if (!props) if (!props)
props = {}; props = {};
props.row = 2; props.row = 2;
props.col = 2; props.col = 1;
this._table.add_style_class_name('multi-line-notification'); this._table.add_style_class_name('multi-line-notification');
this._table.add(this._actionArea, props); this._table.add(this._actionArea, props);
this._updateLastColumnSettings();
this._updated(); 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: // addButton:
// @id: the action ID // @id: the action ID
// @label: the label for the action's button // @label: the label for the action's button
@ -738,9 +657,7 @@ Notification.prototype = {
let box = new St.BoxLayout({ name: 'notification-actions' }); let box = new St.BoxLayout({ name: 'notification-actions' });
this.setActionArea(box, { x_expand: false, this.setActionArea(box, { x_expand: false,
y_expand: false,
x_fill: false, x_fill: false,
y_fill: false,
x_align: St.Align.END }); x_align: St.Align.END });
this._buttonBox = box; this._buttonBox = box;
} }
@ -799,11 +716,10 @@ Notification.prototype = {
}, },
_bannerBoxAllocate: function(actor, box, flags) { _bannerBoxAllocate: function(actor, box, flags) {
let availWidth = box.x2 - box.x1;
let [titleMinW, titleNatW] = this._titleLabel.get_preferred_width(-1); let [titleMinW, titleNatW] = this._titleLabel.get_preferred_width(-1);
let [titleMinH, titleNatH] = this._titleLabel.get_preferred_height(availWidth); let [titleMinH, titleNatH] = this._titleLabel.get_preferred_height(-1);
let [bannerMinW, bannerNatW] = this._bannerLabel.get_preferred_width(availWidth); let [bannerMinW, bannerNatW] = this._bannerLabel.get_preferred_width(-1);
let availWidth = box.x2 - box.x1;
let titleBox = new Clutter.ActorBox(); let titleBox = new Clutter.ActorBox();
let titleBoxW = Math.min(titleNatW, availWidth); let titleBoxW = Math.min(titleNatW, availWidth);
@ -1178,7 +1094,9 @@ SummaryItem.prototype = {
this.notificationStack = new St.BoxLayout({ name: 'summary-notification-stack', this.notificationStack = new St.BoxLayout({ name: 'summary-notification-stack',
vertical: true }); vertical: true });
this.notificationStackView.add_actor(this.notificationStack); this.notificationStackView.add_actor(this.notificationStack);
this._stackedNotifications = []; this._notificationExpandedIds = [];
this._notificationDoneDisplayingIds = [];
this._notificationDestroyedIds = [];
this._oldMaxScrollAdjustment = 0; this._oldMaxScrollAdjustment = 0;
@ -1247,19 +1165,18 @@ SummaryItem.prototype = {
doneShowingNotificationStack: function() { doneShowingNotificationStack: function() {
let notificationActors = this.notificationStack.get_children(); let notificationActors = this.notificationStack.get_children();
for (let i = 0; i < this._stackedNotifications.length; i++) { for (let i = 0; i < notificationActors.length; i++) {
let stackedNotification = this._stackedNotifications[i]; notificationActors[i]._delegate.collapseCompleted();
let notification = stackedNotification.notification; notificationActors[i]._delegate.disconnect(this._notificationExpandedIds[i]);
notification.collapseCompleted(); notificationActors[i]._delegate.disconnect(this._notificationDoneDisplayingIds[i]);
notification.disconnect(stackedNotification.notificationExpandedId); notificationActors[i]._delegate.disconnect(this._notificationDestroyedIds[i]);
notification.disconnect(stackedNotification.notificationDoneDisplayingId); this.notificationStack.remove_actor(notificationActors[i]);
notification.disconnect(stackedNotification.notificationDestroyedId); notificationActors[i]._delegate.setIconVisible(true);
if (notification.actor.get_parent() == this.notificationStack) notificationActors[i]._delegate.enableScrolling(true);
this.notificationStack.remove_actor(notification.actor);
notification.setIconVisible(true);
notification.enableScrolling(true);
} }
this._stackedNotifications = []; this._notificationExpandedIds = [];
this._notificationDoneDisplayingIds = [];
this._notificationDestroyedIds = [];
}, },
_notificationAddedToSource: function(source, notification) { _notificationAddedToSource: function(source, notification) {
@ -1268,12 +1185,12 @@ SummaryItem.prototype = {
}, },
_appendNotificationToStack: function(notification) { _appendNotificationToStack: function(notification) {
let stackedNotification = {}; let notificationExpandedId = notification.connect('expanded', Lang.bind(this, this._contentUpdated));
stackedNotification.notification = notification; this._notificationExpandedIds.push(notificationExpandedId);
stackedNotification.notificationExpandedId = notification.connect('expanded', Lang.bind(this, this._contentUpdated)); let notificationDoneDisplayingId = notification.connect('done-displaying', Lang.bind(this, this._notificationDoneDisplaying));
stackedNotification.notificationDoneDisplayingId = notification.connect('done-displaying', Lang.bind(this, this._notificationDoneDisplaying)); this._notificationDoneDisplayingIds.push(notificationDoneDisplayingId);
stackedNotification.notificationDestroyedId = notification.connect('destroy', Lang.bind(this, this._notificationDestroyed)); let notificationDestroyedId = notification.connect('destroy', Lang.bind(this, this._notificationDestroyed));
this._stackedNotifications.push(stackedNotification); this._notificationDestroyedIds.push(notificationDestroyedId);
if (!this.source.isChat) if (!this.source.isChat)
notification.enableScrolling(false); notification.enableScrolling(false);
if (this.notificationStack.get_children().length > 0) if (this.notificationStack.get_children().length > 0)
@ -1303,18 +1220,17 @@ SummaryItem.prototype = {
}, },
_notificationDestroyed: function(notification) { _notificationDestroyed: function(notification) {
for (let i = 0; i < this._stackedNotifications.length; i++) { let index = this.notificationStack.get_children().indexOf(notification.actor);
if (this._stackedNotifications[i].notification == notification) { if (index >= 0) {
let stackedNotification = this._stackedNotifications[i]; notification.disconnect(this._notificationExpandedIds[index]);
notification.disconnect(stackedNotification.notificationExpandedId); this._notificationExpandedIds.splice(index, 1);
notification.disconnect(stackedNotification.notificationDoneDisplayingId); notification.disconnect(this._notificationDoneDisplayingIds[index]);
notification.disconnect(stackedNotification.notificationDestroyedId); this._notificationDoneDisplayingIds.splice(index, 1);
this._stackedNotifications.splice(i, 1); notification.disconnect(this._notificationDestroyedIds[index]);
this._contentUpdated(); this._notificationDestroyedIds.splice(index, 1);
break; this.notificationStack.remove_actor(notification.actor);
} this._contentUpdated();
} }
if (this.notificationStack.get_children().length > 0) if (this.notificationStack.get_children().length > 0)
this.notificationStack.get_children()[0]._delegate.setIconVisible(true); this.notificationStack.get_children()[0]._delegate.setIconVisible(true);
} }
@ -1356,14 +1272,14 @@ MessageTray.prototype = {
this._summaryBin.opacity = 0; this._summaryBin.opacity = 0;
this._summaryMotionId = 0; this._summaryMotionId = 0;
this._trayMotionId = 0;
this._summaryBoxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM, this._summaryBoxPointer = new BoxPointer.BoxPointer(St.Side.BOTTOM,
{ reactive: true, { reactive: true,
track_hover: true }); track_hover: true });
this._summaryBoxPointer.actor.style_class = 'summary-boxpointer'; this._summaryBoxPointer.actor.style_class = 'summary-boxpointer';
this.actor.add_actor(this._summaryBoxPointer.actor);
this._summaryBoxPointer.actor.lower_bottom();
this._summaryBoxPointer.actor.hide(); this._summaryBoxPointer.actor.hide();
Main.layoutManager.addChrome(this._summaryBoxPointer.actor, { visibleInFullscreen: true });
this._summaryBoxPointerItem = null; this._summaryBoxPointerItem = null;
this._summaryBoxPointerContentUpdatedId = 0; this._summaryBoxPointerContentUpdatedId = 0;
@ -1385,9 +1301,9 @@ MessageTray.prototype = {
this._focusGrabber.connect('focus-grabbed', Lang.bind(this, this._focusGrabber.connect('focus-grabbed', Lang.bind(this,
function() { function() {
if (this._summaryBoxPointer.bin.child) if (this._summaryBoxPointer.bin.child)
this._lock(); this.lock();
})); }));
this._focusGrabber.connect('focus-ungrabbed', Lang.bind(this, this._unlock)); this._focusGrabber.connect('focus-ungrabbed', Lang.bind(this, this.unlock));
this._focusGrabber.connect('button-pressed', Lang.bind(this, this._focusGrabber.connect('button-pressed', Lang.bind(this,
function(focusGrabber, source) { function(focusGrabber, source) {
if (this._clickedSummaryItem && !this._clickedSummaryItem.actor.contains(source)) if (this._clickedSummaryItem && !this._clickedSummaryItem.actor.contains(source))
@ -1396,15 +1312,11 @@ MessageTray.prototype = {
})); }));
this._focusGrabber.connect('escape-pressed', Lang.bind(this, this._escapeTray)); this._focusGrabber.connect('escape-pressed', Lang.bind(this, this._escapeTray));
Main.layoutManager.keyboardBox.connect('notify::hover', Lang.bind(this, this._onKeyboardHoverChanged));
this._trayState = State.HIDDEN; this._trayState = State.HIDDEN;
this._locked = false; this._locked = 0;
this._traySummoned = false;
this._useLongerTrayLeftTimeout = false; this._useLongerTrayLeftTimeout = false;
this._trayLeftTimeoutId = 0; this._trayLeftTimeoutId = 0;
this._pointerInTray = false; this._pointerInTray = false;
this._pointerInKeyboard = false;
this._summaryState = State.HIDDEN; this._summaryState = State.HIDDEN;
this._summaryTimeoutId = 0; this._summaryTimeoutId = 0;
this._pointerInSummary = false; this._pointerInSummary = false;
@ -1417,10 +1329,10 @@ MessageTray.prototype = {
this._notificationRemoved = false; this._notificationRemoved = false;
this._reNotifyAfterHideNotification = null; this._reNotifyAfterHideNotification = null;
Main.layoutManager.trayBox.add_actor(this.actor); Main.layoutManager.topBox.add_actor(this.actor);
this.actor.y = -1; Main.chrome.trackActor(this._notificationBin);
Main.layoutManager.trackChrome(this.actor); Main.chrome.trackActor(this._summaryBin);
Main.layoutManager.trackChrome(this._notificationBin); Main.chrome.trackActor(this._summaryBoxPointer.actor);
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._setSizePosition)); Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._setSizePosition));
@ -1429,9 +1341,9 @@ MessageTray.prototype = {
Main.overview.connect('showing', Lang.bind(this, Main.overview.connect('showing', Lang.bind(this,
function() { function() {
this._overviewVisible = true; this._overviewVisible = true;
if (this._locked) { if (this._locked > 0) {
this._unsetClickedSummaryItem(); this._unsetClickedSummaryItem();
this._unlock(); this.unlock();
} else { } else {
this._updateState(); this._updateState();
} }
@ -1439,9 +1351,9 @@ MessageTray.prototype = {
Main.overview.connect('hiding', Lang.bind(this, Main.overview.connect('hiding', Lang.bind(this,
function() { function() {
this._overviewVisible = false; this._overviewVisible = false;
if (this._locked) { if (this._locked > 0) {
this._unsetClickedSummaryItem(); this._unsetClickedSummaryItem();
this._unlock(); this.unlock();
} else { } else {
this._updateState(); this._updateState();
} }
@ -1459,10 +1371,22 @@ MessageTray.prototype = {
_setSizePosition: function() { _setSizePosition: function() {
let monitor = Main.layoutManager.bottomMonitor; let monitor = Main.layoutManager.bottomMonitor;
this.actor.x = monitor.x;
this.actor.y = -1;
this.actor.width = monitor.width;
this._notificationBin.x = 0; this._notificationBin.x = 0;
this._notificationBin.width = monitor.width; this._notificationBin.width = monitor.width;
this._summaryBin.x = 0; this._summaryBin.x = 0;
this._summaryBin.width = monitor.width; 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) { contains: function(source) {
@ -1507,11 +1431,8 @@ MessageTray.prototype = {
// after notifications are done showing. However, we don't want that to happen for // 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 // 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. // 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. // So we just don't add transient sources to this._newSummaryItems .
// We don't want that to happen for chat sources neither, because they if (!source.isTransient)
// 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); this._newSummaryItems.push(summaryItem);
source.connect('notify', Lang.bind(this, this._onNotify)); source.connect('notify', Lang.bind(this, this._onNotify));
@ -1603,27 +1524,16 @@ MessageTray.prototype = {
this._notificationQueue.splice(index, 1); this._notificationQueue.splice(index, 1);
}, },
_lock: function() { lock: function() {
this._locked = true; this._locked += 1;
},
_unlock: function() {
if (!this._locked)
return;
this._locked = false;
this._pointerInTray = this.actor.hover;
this._updateState(); this._updateState();
}, },
toggle: function() { unlock: function() {
this._traySummoned = !this._traySummoned; if (this._locked > 0)
this._updateState(); this._locked -= 1;
}, this._pointerInSummary = false;
this._pointerInTray = false;
hide: function() {
this._traySummoned = false;
this.actor.set_hover(false);
this._summary.set_hover(false);
this._updateState(); this._updateState();
}, },
@ -1811,6 +1721,13 @@ MessageTray.prototype = {
if (this._useLongerTrayLeftTimeout && !this._trayLeftTimeoutId) if (this._useLongerTrayLeftTimeout && !this._trayLeftTimeoutId)
return; 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; this._useLongerTrayLeftTimeout = false;
if (this._trayLeftTimeoutId) { if (this._trayLeftTimeoutId) {
Mainloop.source_remove(this._trayLeftTimeoutId); Mainloop.source_remove(this._trayLeftTimeoutId);
@ -1854,24 +1771,6 @@ MessageTray.prototype = {
} }
}, },
_onKeyboardHoverChanged: function(keyboard) {
this._pointerInKeyboard = keyboard.hover;
if (!keyboard.hover) {
let event = Clutter.get_current_event();
if (event && event.type() == Clutter.EventType.LEAVE) {
let into = event.get_related();
if (into && this.actor.contains(into)) {
// Don't call _updateState, because pointerInTray is
// still false
return;
}
}
}
this._updateState();
},
_onStatusChanged: function(presence, status) { _onStatusChanged: function(presence, status) {
this._backFromAway = (this._userStatus == GnomeSession.PresenceStatus.IDLE && this._userStatus != status); this._backFromAway = (this._userStatus == GnomeSession.PresenceStatus.IDLE && this._userStatus != status);
this._userStatus = status; this._userStatus = status;
@ -1918,7 +1817,7 @@ MessageTray.prototype = {
}, },
_escapeTray: function() { _escapeTray: function() {
this._unlock(); this.unlock();
this._pointerInTray = false; this._pointerInTray = false;
this._pointerInSummary = false; this._pointerInSummary = false;
this._updateNotificationTimeout(0); this._updateNotificationTimeout(0);
@ -1936,7 +1835,7 @@ MessageTray.prototype = {
let notificationsPending = this._notificationQueue.length > 0 && (!this._busy || notificationUrgent); let notificationsPending = this._notificationQueue.length > 0 && (!this._busy || notificationUrgent);
let notificationPinned = this._pointerInTray && !this._pointerInSummary && !this._notificationRemoved; let notificationPinned = this._pointerInTray && !this._pointerInSummary && !this._notificationRemoved;
let notificationExpanded = this._notificationBin.y < 0; let notificationExpanded = this._notificationBin.y < 0;
let notificationExpired = (this._notificationTimeoutId == 0 && !(this._notification && this._notification.urgency == Urgency.CRITICAL) && !this._pointerInTray && !this._locked && !(this._pointerInKeyboard && notificationExpanded)) || this._notificationRemoved; let notificationExpired = (this._notificationTimeoutId == 0 && !(this._notification && this._notification.urgency == Urgency.CRITICAL) && !this._pointerInTray && (this._locked == 0)) || this._notificationRemoved;
let canShowNotification = notificationsPending && this._summaryState == State.HIDDEN; let canShowNotification = notificationsPending && this._summaryState == State.HIDDEN;
if (this._notificationState == State.HIDDEN) { if (this._notificationState == State.HIDDEN) {
@ -1952,17 +1851,17 @@ MessageTray.prototype = {
} }
// Summary // Summary
let summarySummoned = this._pointerInSummary || this._overviewVisible || this._traySummoned; let summarySummoned = this._pointerInSummary || this._overviewVisible || (this._locked > 0);
let summaryPinned = this._summaryTimeoutId != 0 || this._pointerInTray || summarySummoned || this._locked; let summaryPinned = this._summaryTimeoutId != 0 || this._pointerInTray || summarySummoned;
let summaryHovered = this._pointerInTray || this._pointerInSummary; let summaryHovered = this._pointerInTray || this._pointerInSummary;
let summaryVisibleWithNoHover = (this._overviewVisible || this._locked) && !summaryHovered; let summaryVisibleWithNoHover = (this._overviewVisible || this._locked > 0) && !summaryHovered;
let summaryNotificationIsForExpandedSummaryItem = (this._clickedSummaryItem == this._expandedSummaryItem); let summaryNotificationIsForExpandedSummaryItem = (this._clickedSummaryItem == this._expandedSummaryItem);
let notificationsVisible = (this._notificationState == State.SHOWING || let notificationsVisible = (this._notificationState == State.SHOWING ||
this._notificationState == State.SHOWN); this._notificationState == State.SHOWN);
let notificationsDone = !notificationsVisible && !notificationsPending; let notificationsDone = !notificationsVisible && !notificationsPending;
let summaryOptionalInOverview = this._overviewVisible && !this._locked && !summaryHovered; let summaryOptionalInOverview = this._overviewVisible && (this._locked == 0) && !summaryHovered;
let mustHideSummary = (notificationsPending && (notificationUrgent || summaryOptionalInOverview)) let mustHideSummary = (notificationsPending && (notificationUrgent || summaryOptionalInOverview))
|| notificationsVisible; || notificationsVisible;
@ -2047,7 +1946,7 @@ MessageTray.prototype = {
_showTray: function() { _showTray: function() {
this._tween(this.actor, '_trayState', State.SHOWN, this._tween(this.actor, '_trayState', State.SHOWN,
{ y: -this.actor.height, { y: 0,
time: ANIMATION_TIME, time: ANIMATION_TIME,
transition: 'easeOutQuad' transition: 'easeOutQuad'
}); });
@ -2055,7 +1954,7 @@ MessageTray.prototype = {
_hideTray: function() { _hideTray: function() {
this._tween(this.actor, '_trayState', State.HIDDEN, this._tween(this.actor, '_trayState', State.HIDDEN,
{ y: -1, { y: Main.layoutManager.topBox.height - 1,
time: ANIMATION_TIME, time: ANIMATION_TIME,
transition: 'easeOutQuad' transition: 'easeOutQuad'
}); });
@ -2143,7 +2042,7 @@ MessageTray.prototype = {
_notificationTimeout: function() { _notificationTimeout: function() {
let [x, y, mods] = global.get_pointer(); let [x, y, mods] = global.get_pointer();
if (y > this._lastSeenMouseY + 10 && !this.actor.hover) { if (y > this._lastSeenMouseY + 10 && y < this.actor.y) {
// The mouse is moving towards the notification, so don't // The mouse is moving towards the notification, so don't
// hide it yet. (We just create a new timeout (and destroy // hide it yet. (We just create a new timeout (and destroy
// the old one) each time because the bookkeeping is // the old one) each time because the bookkeeping is
@ -2271,7 +2170,7 @@ MessageTray.prototype = {
_showSummaryBoxPointer: function() { _showSummaryBoxPointer: function() {
this._summaryBoxPointerItem = this._clickedSummaryItem; this._summaryBoxPointerItem = this._clickedSummaryItem;
this._summaryBoxPointerContentUpdatedId = this._summaryBoxPointerItem.connect('content-updated', this._summaryBoxPointerContentUpdatedId = this._summaryBoxPointerItem.connect('content-updated',
Lang.bind(this, this._onSummaryBoxPointerContentUpdated)); Lang.bind(this, this._adjustSummaryBoxPointerPosition));
this._summaryBoxPointerDoneDisplayingId = this._summaryBoxPointerItem.connect('done-displaying-content', this._summaryBoxPointerDoneDisplayingId = this._summaryBoxPointerItem.connect('done-displaying-content',
Lang.bind(this, this._escapeTray)); Lang.bind(this, this._escapeTray));
if (this._clickedSummaryItemMouseButton == 1) { if (this._clickedSummaryItemMouseButton == 1) {
@ -2294,8 +2193,6 @@ MessageTray.prototype = {
// _clickedSummaryItem.actor can change absolute position without changing allocation // _clickedSummaryItem.actor can change absolute position without changing allocation
this._summaryMotionId = this._summary.connect('allocation-changed', this._summaryMotionId = this._summary.connect('allocation-changed',
Lang.bind(this, this._adjustSummaryBoxPointerPosition)); Lang.bind(this, this._adjustSummaryBoxPointerPosition));
this._trayMotionId = Main.layoutManager.trayBox.connect('notify::anchor-y',
Lang.bind(this, this._adjustSummaryBoxPointerPosition));
this._summaryBoxPointer.actor.opacity = 0; this._summaryBoxPointer.actor.opacity = 0;
this._summaryBoxPointer.actor.show(); this._summaryBoxPointer.actor.show();
@ -2308,13 +2205,6 @@ MessageTray.prototype = {
})); }));
}, },
_onSummaryBoxPointerContentUpdated: function() {
if (this._summaryBoxPointerItem.notificationStack.get_children().length == 0)
this._hideSummaryBoxPointer();
this._adjustSummaryBoxPointerPosition();
},
_adjustSummaryBoxPointerPosition: function() { _adjustSummaryBoxPointerPosition: function() {
// The position of the arrow origin should be the same as center of this._clickedSummaryItem.actor // The position of the arrow origin should be the same as center of this._clickedSummaryItem.actor
if (!this._clickedSummaryItem) if (!this._clickedSummaryItem)
@ -2327,10 +2217,8 @@ MessageTray.prototype = {
if (this._clickedSummaryItemAllocationChangedId) { if (this._clickedSummaryItemAllocationChangedId) {
this._clickedSummaryItem.actor.disconnect(this._clickedSummaryItemAllocationChangedId); this._clickedSummaryItem.actor.disconnect(this._clickedSummaryItemAllocationChangedId);
this._summary.disconnect(this._summaryMotionId); this._summary.disconnect(this._summaryMotionId);
Main.layoutManager.trayBox.disconnect(this._trayMotionId);
this._clickedSummaryItemAllocationChangedId = 0; this._clickedSummaryItemAllocationChangedId = 0;
this._summaryMotionId = 0; this._summaryMotionId = 0;
this._trayMotionId = 0;
} }
if (this._clickedSummaryItem) if (this._clickedSummaryItem)
@ -2340,18 +2228,9 @@ MessageTray.prototype = {
}, },
_hideSummaryBoxPointer: function() { _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; this._summaryBoxPointerState = State.HIDING;
// Unset this._clickedSummaryItem if we are no longer showing the summary or if // Unset this._clickedSummaryItem if we are no longer showing the summary
// this._clickedSummaryItem is still the item associated with the currently showing box pointer if (this._summaryState != State.SHOWN)
if (this._summaryState != State.SHOWN || this._summaryBoxPointerItem == this._clickedSummaryItem)
this._unsetClickedSummaryItem(); this._unsetClickedSummaryItem();
this._focusGrabber.ungrabFocus(); this._focusGrabber.ungrabFocus();

View File

@ -18,7 +18,6 @@ const Main = imports.ui.main;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const OPEN_AND_CLOSE_TIME = 0.1; const OPEN_AND_CLOSE_TIME = 0.1;
const FADE_IN_BUTTONS_TIME = 0.33;
const FADE_OUT_DIALOG_TIME = 1.0; const FADE_OUT_DIALOG_TIME = 1.0;
const State = { const State = {
@ -35,12 +34,10 @@ function ModalDialog() {
ModalDialog.prototype = { ModalDialog.prototype = {
_init: function(params) { _init: function(params) {
params = Params.parse(params, { shellReactive: false, params = Params.parse(params, { styleClass: null });
styleClass: null });
this.state = State.CLOSED; this.state = State.CLOSED;
this._hasModal = false; this._hasModal = false;
this._shellReactive = params.shellReactive;
this._group = new St.Group({ visible: false, this._group = new St.Group({ visible: false,
x: 0, x: 0,
@ -56,30 +53,26 @@ ModalDialog.prototype = {
this._actionKeys = {}; this._actionKeys = {};
this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent)); 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._backgroundBin = new St.Bin();
this._group.add_actor(this._backgroundBin); 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', this._dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog',
vertical: true }); vertical: true });
if (params.styleClass != null) { if (params.styleClass != null) {
this._dialogLayout.add_style_class_name(params.styleClass); 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.contentLayout = new St.BoxLayout({ vertical: true });
this._dialogLayout.add(this.contentLayout, this._dialogLayout.add(this.contentLayout,
@ -89,6 +82,7 @@ ModalDialog.prototype = {
y_align: St.Align.START }); y_align: St.Align.START });
this._buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box', this._buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box',
opacity: 220,
vertical: false }); vertical: false });
this._dialogLayout.add(this._buttonLayout, this._dialogLayout.add(this._buttonLayout,
{ expand: true, { expand: true,
@ -100,26 +94,21 @@ ModalDialog.prototype = {
this._savedKeyFocus = null; this._savedKeyFocus = null;
}, },
destroy: function() {
this._group.destroy();
},
setButtons: function(buttons) { setButtons: function(buttons) {
let hadChildren = this._buttonLayout.get_children() > 0;
this._buttonLayout.destroy_children(); this._buttonLayout.destroy_children();
this._actionKeys = {}; this._actionKeys = {};
for (let i = 0; i < buttons.length; i ++) { let i = 0;
let buttonInfo = buttons[i]; for (let index in buttons) {
let buttonInfo = buttons[index];
let label = buttonInfo['label']; let label = buttonInfo['label'];
let action = buttonInfo['action']; let action = buttonInfo['action'];
let key = buttonInfo['key']; let key = buttonInfo['key'];
buttonInfo.button = new St.Button({ style_class: 'modal-dialog-button', let button = new St.Button({ style_class: 'modal-dialog-button',
reactive: true, reactive: true,
can_focus: true, can_focus: true,
label: label }); label: label });
let x_alignment; let x_alignment;
if (buttons.length == 1) if (buttons.length == 1)
@ -131,37 +120,20 @@ ModalDialog.prototype = {
else else
x_alignment = St.Align.MIDDLE; x_alignment = St.Align.MIDDLE;
if (this._initialKeyFocus == this._dialogLayout || this._initialKeyFocus = button;
this._buttonLayout.contains(this._initialKeyFocus)) this._buttonLayout.add(button,
this._initialKeyFocus = buttonInfo.button;
this._buttonLayout.add(buttonInfo.button,
{ expand: true, { expand: true,
x_fill: false, x_fill: false,
y_fill: false, y_fill: false,
x_align: x_alignment, x_align: x_alignment,
y_align: St.Align.MIDDLE }); y_align: St.Align.MIDDLE });
buttonInfo.button.connect('clicked', action); button.connect('clicked', action);
if (key) if (key)
this._actionKeys[key] = action; 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) { _onKeyPressEvent: function(object, keyPressEvent) {
@ -185,8 +157,7 @@ ModalDialog.prototype = {
this.state = State.OPENING; this.state = State.OPENING;
this._dialogLayout.opacity = 255; this._dialogLayout.opacity = 255;
if (this._lightbox) this._lightbox.show();
this._lightbox.show();
this._group.opacity = 0; this._group.opacity = 0;
this._group.show(); this._group.show();
Tweener.addTween(this._group, Tweener.addTween(this._group,
@ -252,8 +223,7 @@ ModalDialog.prototype = {
global.gdk_screen.get_display().sync(); global.gdk_screen.get_display().sync();
this._hasModal = false; this._hasModal = false;
if (!this._shellReactive) this._eventBlocker.raise_top();
this._eventBlocker.raise_top();
}, },
pushModal: function (timestamp) { pushModal: function (timestamp) {
@ -269,8 +239,7 @@ ModalDialog.prototype = {
} else } else
this._initialKeyFocus.grab_key_focus(); this._initialKeyFocus.grab_key_focus();
if (!this._shellReactive) this._eventBlocker.lower_bottom();
this._eventBlocker.lower_bottom();
return true; return true;
}, },

View File

@ -1,407 +0,0 @@
/* -*- 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

@ -97,8 +97,10 @@ NotificationDaemon.prototype = {
this._notifications = {}; this._notifications = {};
this._busProxy = new Bus(); this._busProxy = new Bus();
Main.statusIconDispatcher.connect('message-icon-added', Lang.bind(this, this._onTrayIconAdded)); Main.connect('initialized', Lang.bind(this, function() {
Main.statusIconDispatcher.connect('message-icon-removed', Lang.bind(this, this._onTrayIconRemoved)); Main.statusIconDispatcher.connect('message-icon-added', Lang.bind(this, this._onTrayIconAdded));
Main.statusIconDispatcher.connect('message-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
}));
Shell.WindowTracker.get_default().connect('notify::focus-app', Shell.WindowTracker.get_default().connect('notify::focus-app',
Lang.bind(this, this._onFocusAppChanged)); Lang.bind(this, this._onFocusAppChanged));
@ -119,6 +121,11 @@ NotificationDaemon.prototype = {
return new St.Icon({ icon_name: icon, return new St.Icon({ icon_name: icon,
icon_type: St.IconType.FULLCOLOR, icon_type: St.IconType.FULLCOLOR,
icon_size: size }); 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 { } else {
let stockIcon; let stockIcon;
switch (hints.urgency) { switch (hints.urgency) {
@ -190,8 +197,6 @@ NotificationDaemon.prototype = {
if (appName == 'Empathy' && (hints['category'] == 'im.received' || if (appName == 'Empathy' && (hints['category'] == 'im.received' ||
hints['category'] == 'x-empathy.im.room-invitation' || hints['category'] == 'x-empathy.im.room-invitation' ||
hints['category'] == 'x-empathy.call.incoming' || 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.online' ||
hints['category'] == 'presence.offline')) { hints['category'] == 'presence.offline')) {
// Ignore replacesId since we already sent back a // Ignore replacesId since we already sent back a
@ -215,18 +220,14 @@ NotificationDaemon.prototype = {
hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true); hints = Params.parse(hints, { urgency: Urgency.NORMAL }, true);
// Be compatible with the various hints for image data and image path // Be compatible with the various hints for image data
// 'image-data' and 'image-path' are the latest name of these hints, introduced in 1.2 // 'image-data' is the latest name of this hint, introduced in 1.2
if (!hints['image-data']) {
if (!hints['image-path'] && hints['image_path'])
hints['image-path'] = hints['image_path']; // version 1.1 of the spec
if (!hints['image-data'])
if (hints['image_data']) if (hints['image_data'])
hints['image-data'] = hints['image_data']; // version 1.1 of the spec hints['image-data'] = hints['image_data']; // version 1.1 of the spec
else if (hints['icon_data'] && !hints['image-path']) else if (hints['icon_data'])
// early versions of the spec; 'icon_data' should only be used if 'image-path' is not available hints['image-data'] = hints['icon_data']; // previous versions of the spec
hints['image-data'] = hints['icon_data']; }
let ndata = { appName: appName, let ndata = { appName: appName,
icon: icon, icon: icon,
@ -305,7 +306,7 @@ NotificationDaemon.prototype = {
ndata.notification = notification; ndata.notification = notification;
notification.connect('destroy', Lang.bind(this, notification.connect('destroy', Lang.bind(this,
function(n, reason) { function(n, reason) {
delete this._notifications[ndata.id]; delete this._notifications[id];
let notificationClosedReason; let notificationClosedReason;
switch (reason) { switch (reason) {
case MessageTray.NotificationDestroyedReason.EXPIRED: case MessageTray.NotificationDestroyedReason.EXPIRED:
@ -318,11 +319,11 @@ NotificationDaemon.prototype = {
notificationClosedReason = NotificationClosedReason.APP_CLOSED; notificationClosedReason = NotificationClosedReason.APP_CLOSED;
break; break;
} }
this._emitNotificationClosed(ndata.id, notificationClosedReason); this._emitNotificationClosed(id, notificationClosedReason);
})); }));
notification.connect('action-invoked', Lang.bind(this, notification.connect('action-invoked', Lang.bind(this,
function(n, actionId) { function(n, actionId) {
this._emitActionInvoked(ndata.id, actionId); this._emitActionInvoked(id, actionId);
})); }));
} else { } else {
notification.update(summary, body, { icon: iconActor, notification.update(summary, body, { icon: iconActor,
@ -330,34 +331,10 @@ NotificationDaemon.prototype = {
clear: true }); 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) { if (actions.length) {
notification.setUseActionIcons(hints['action-icons'] == true); notification.setUseActionIcons(hints['action-icons'] == true);
for (let i = 0; i < actions.length - 1; i += 2) { for (let i = 0; i < actions.length - 1; i += 2)
if (actions[i] == 'default') notification.addButton(actions[i], actions[i + 1]);
notification.connect('clicked', Lang.bind(this,
function() {
this._emitActionInvoked(ndata.id, "default");
}));
else
notification.addButton(actions[i], actions[i + 1]);
}
} }
switch (hints.urgency) { switch (hints.urgency) {
case Urgency.LOW: case Urgency.LOW:
@ -486,11 +463,9 @@ Source.prototype = {
_onNameVanished: function() { _onNameVanished: function() {
// Destroy the notification source when its sender is removed from DBus. // 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, // 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. // so allow the code path that handles the tray icon being removed to handle that case.
if (!this.trayIcon && this.app) if (!this.trayIcon)
this.destroy(); this.destroy();
}, },

View File

@ -11,7 +11,6 @@ const Shell = imports.gi.Shell;
const Gdk = imports.gi.Gdk; const Gdk = imports.gi.Gdk;
const AppDisplay = imports.ui.appDisplay; const AppDisplay = imports.ui.appDisplay;
const ContactDisplay = imports.ui.contactDisplay;
const Dash = imports.ui.dash; const Dash = imports.ui.dash;
const DND = imports.ui.dnd; const DND = imports.ui.dnd;
const DocDisplay = imports.ui.docDisplay; const DocDisplay = imports.ui.docDisplay;
@ -19,7 +18,6 @@ const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main; const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray; const MessageTray = imports.ui.messageTray;
const Panel = imports.ui.panel; const Panel = imports.ui.panel;
const Params = imports.misc.params;
const PlaceDisplay = imports.ui.placeDisplay; const PlaceDisplay = imports.ui.placeDisplay;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const ViewSelector = imports.ui.viewSelector; const ViewSelector = imports.ui.viewSelector;
@ -98,30 +96,14 @@ ShellInfo.prototype = {
}; };
function Overview() { function Overview() {
this._init.apply(this, arguments); this._init();
} }
Overview.prototype = { Overview.prototype = {
_init : function(params) { _init : function() {
params = Params.parse(params, { isDummy: false }); // The actual global.background_actor is inside global.window_group,
// which is hidden when displaying the overview, so we display a clone.
this.isDummy = params.isDummy; this._background = new Clutter.Clone({ source: global.background_actor });
// 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(); this._background.hide();
global.overlay_group.add_actor(this._background); global.overlay_group.add_actor(this._background);
@ -171,6 +153,18 @@ Overview.prototype = {
this._coverPane.hide(); this._coverPane.hide();
this._windowSwitchTimeoutId = 0;
this._windowSwitchTimestamp = 0;
this._lastActiveWorkspaceIndex = -1;
this._lastHoveredWindow = null;
this._needsFakePointerEvent = false;
this.workspaces = null;
Main.connect('initialized', Lang.bind(this, this._finishInit));
},
_finishInit: function() {
// XDND // XDND
this._dragMonitor = { this._dragMonitor = {
dragMotion: Lang.bind(this, this._onDragMotion) dragMotion: Lang.bind(this, this._onDragMotion)
@ -179,75 +173,40 @@ Overview.prototype = {
Main.xdndHandler.connect('drag-begin', Lang.bind(this, this._onDragBegin)); Main.xdndHandler.connect('drag-begin', Lang.bind(this, this._onDragBegin));
Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd)); Main.xdndHandler.connect('drag-end', Lang.bind(this, this._onDragEnd));
this._windowSwitchTimeoutId = 0; this.shellInfo = new ShellInfo();
this._windowSwitchTimestamp = 0;
this._lastActiveWorkspaceIndex = -1;
this._lastHoveredWindow = null;
this._needsFakePointerEvent = false;
this.workspaces = null; // The members we construct that are implemented in JS might
}, // want to access the overview as Main.overview to connect
// signal handlers and so forth. So we create them here.
// The members we construct that are implemented in JS might this.viewSelector = new ViewSelector.ViewSelector();
// want to access the overview as Main.overview to connect this._group.add_actor(this.viewSelector.actor);
// signal handlers and so forth. So we create them after
// construction in this init() method.
init: function() {
if (this.isDummy)
return;
this._shellInfo = new ShellInfo();
this._viewSelector = new ViewSelector.ViewSelector();
this._group.add_actor(this._viewSelector.actor);
this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay(); 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(); 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 // Default search providers
this.addSearchProvider(new AppDisplay.AppSearchProvider()); this.viewSelector.addSearchProvider(new AppDisplay.AppSearchProvider());
this.addSearchProvider(new AppDisplay.SettingsSearchProvider()); this.viewSelector.addSearchProvider(new AppDisplay.PrefsSearchProvider());
this.addSearchProvider(new PlaceDisplay.PlaceSearchProvider()); this.viewSelector.addSearchProvider(new PlaceDisplay.PlaceSearchProvider());
this.addSearchProvider(new DocDisplay.DocSearchProvider()); this.viewSelector.addSearchProvider(new DocDisplay.DocSearchProvider());
this.addSearchProvider(new ContactDisplay.ContactSearchProvider());
// TODO - recalculate everything when desktop size changes // TODO - recalculate everything when desktop size changes
this._dash = new Dash.Dash(); this.dash = new Dash.Dash();
this._group.add_actor(this._dash.actor); this._group.add_actor(this.dash.actor);
this._dash.actor.add_constraint(this._viewSelector.constrainY); this.dash.actor.add_constraint(this.viewSelector.constrainY);
this._dash.actor.add_constraint(this._viewSelector.constrainHeight); 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 // Translators: this is the name of the dock/favorites area on
// the left of the overview // 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)); Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
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() { _onDragBegin: function() {
DND.addDragMonitor(this._dragMonitor); DND.addDragMonitor(this._dragMonitor);
// Remember the workspace we started from // Remember the workspace we started from
@ -317,9 +276,6 @@ Overview.prototype = {
}, },
setScrollAdjustment: function(adjustment, direction) { setScrollAdjustment: function(adjustment, direction) {
if (this.isDummy)
return;
this._scrollAdjustment = adjustment; this._scrollAdjustment = adjustment;
if (this._scrollAdjustment == null) if (this._scrollAdjustment == null)
this._scrollDirection = SwipeScrollDirection.NONE; this._scrollDirection = SwipeScrollDirection.NONE;
@ -494,33 +450,30 @@ Overview.prototype = {
let primary = Main.layoutManager.primaryMonitor; let primary = Main.layoutManager.primaryMonitor;
let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL); let rtl = (St.Widget.get_default_direction () == St.TextDirection.RTL);
let contentY = Main.panel.actor.height;
let contentHeight = primary.height - contentY - Main.messageTray.actor.height;
this._group.set_position(primary.x, primary.y); this._group.set_position(primary.x, primary.y);
this._group.set_size(primary.width, primary.height); this._group.set_size(primary.width, primary.height);
this._coverPane.set_position(0, contentY); this._coverPane.set_position(primary.x, primary.y);
this._coverPane.set_size(primary.width, contentHeight); this._coverPane.set_size(primary.width, primary.height);
let dashWidth = Math.round(DASH_SPLIT_FRACTION * primary.width); let dashWidth = Math.round(DASH_SPLIT_FRACTION * primary.width);
let viewWidth = primary.width - dashWidth - this._spacing; let viewWidth = primary.width - dashWidth - this._spacing;
let viewHeight = contentHeight - 2 * this._spacing; let viewHeight = primary.height - 2 * this._spacing;
let viewY = contentY + this._spacing; let viewY = primary.y + this._spacing;
let viewX = rtl ? 0 : dashWidth + this._spacing; let viewX = rtl ? 0 : dashWidth + this._spacing;
// Set the dash's x position - y is handled by a constraint // Set the dash's x position - y is handled by a constraint
let dashX; let dashX;
if (rtl) { 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; dashX = primary.width;
} else { } else {
dashX = 0; 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_position(viewX, viewY);
this._viewSelector.actor.set_size(viewWidth, viewHeight); this.viewSelector.actor.set_size(viewWidth, viewHeight);
}, },
//// Public methods //// //// Public methods ////
@ -553,8 +506,6 @@ Overview.prototype = {
// //
// Animates the overview visible and grabs mouse and keyboard input // Animates the overview visible and grabs mouse and keyboard input
show : function() { show : function() {
if (this.isDummy)
return;
if (this._shown) if (this._shown)
return; return;
// Do this manually instead of using _syncInputMode, to handle failure // Do this manually instead of using _syncInputMode, to handle failure
@ -582,9 +533,6 @@ Overview.prototype = {
// //
// If we switched to displaying the actors in the Overview rather than // If we switched to displaying the actors in the Overview rather than
// clones of them, this would obviously no longer be necessary. // 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(); global.window_group.hide();
this._group.show(); this._group.show();
this._background.show(); this._background.show();
@ -616,12 +564,6 @@ Overview.prototype = {
onCompleteScope: this onCompleteScope: this
}); });
Tweener.addTween(this._background,
{ dim_factor: 0.4,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
this._coverPane.raise_top(); this._coverPane.raise_top();
this._coverPane.show(); this._coverPane.show();
this.emit('showing'); this.emit('showing');
@ -634,9 +576,6 @@ Overview.prototype = {
// will result in the overview not being hidden until hideTemporarily() is // will result in the overview not being hidden until hideTemporarily() is
// called. // called.
showTemporarily: function() { showTemporarily: function() {
if (this.isDummy)
return;
if (this._shownTemporarily) if (this._shownTemporarily)
return; return;
@ -649,9 +588,6 @@ Overview.prototype = {
// //
// Reverses the effect of show() // Reverses the effect of show()
hide: function() { hide: function() {
if (this.isDummy)
return;
if (!this._shown) if (!this._shown)
return; return;
@ -670,9 +606,6 @@ Overview.prototype = {
// //
// Reverses the effect of showTemporarily() // Reverses the effect of showTemporarily()
hideTemporarily: function() { hideTemporarily: function() {
if (this.isDummy)
return;
if (!this._shownTemporarily) if (!this._shownTemporarily)
return; return;
@ -684,9 +617,6 @@ Overview.prototype = {
}, },
toggle: function() { toggle: function() {
if (this.isDummy)
return;
if (this._shown) if (this._shown)
this.hide(); this.hide();
else else
@ -752,12 +682,6 @@ Overview.prototype = {
onCompleteScope: this onCompleteScope: this
}); });
Tweener.addTween(this._background,
{ dim_factor: 1.0,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
this._coverPane.raise_top(); this._coverPane.raise_top();
this._coverPane.show(); this._coverPane.show();
this.emit('hiding'); this.emit('hiding');
@ -778,9 +702,6 @@ Overview.prototype = {
}, },
_hideDone: function() { _hideDone: function() {
// Re-enable unredirection
Meta.enable_unredirect_for_screen(global.screen);
global.window_group.show(); global.window_group.show();
this.workspaces.destroy(); this.workspaces.destroy();

View File

@ -16,44 +16,37 @@ const Layout = imports.ui.layout;
const Overview = imports.ui.overview; const Overview = imports.ui.overview;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const StatusMenu = imports.ui.statusMenu;
const DateMenu = imports.ui.dateMenu; const DateMenu = imports.ui.dateMenu;
const Main = imports.ui.main; const Main = imports.ui.main;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const PANEL_ICON_SIZE = 24; const PANEL_ICON_SIZE = 24;
const STARTUP_ANIMATION_TIME = 0.2;
const BUTTON_DND_ACTIVATION_TIMEOUT = 250; const BUTTON_DND_ACTIVATION_TIMEOUT = 250;
const ANIMATED_ICON_UPDATE_TIMEOUT = 100; const ANIMATED_ICON_UPDATE_TIMEOUT = 100;
const SPINNER_ANIMATION_TIME = 0.2; const SPINNER_ANIMATION_TIME = 0.2;
const STANDARD_STATUS_AREA_ORDER = ['a11y', 'keyboard', 'volume', 'bluetooth', 'network', 'battery', 'userMenu']; const STANDARD_TRAY_ICON_ORDER = ['a11y', 'display', 'keyboard', 'volume', 'bluetooth', 'network', 'battery'];
const STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION = { const STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION = {
'a11y': imports.ui.status.accessibility.ATIndicator, 'a11y': imports.ui.status.accessibility.ATIndicator,
'volume': imports.ui.status.volume.Indicator, 'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.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) if (Config.HAVE_BLUETOOTH)
STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION['bluetooth'] = imports.ui.status.bluetooth.Indicator; STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION['bluetooth'] = imports.ui.status.bluetooth.Indicator;
try { try {
STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION['network'] = imports.ui.status.network.NMApplet; STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION['network'] = imports.ui.status.network.NMApplet;
} catch(e) { } catch(e) {
log('NMApplet is not supported. It is possible that your NetworkManager version is too old'); 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, // To make sure the panel corners blend nicely with the panel,
// we draw background and borders the same way, e.g. drawing // we draw background and borders the same way, e.g. drawing
// them as filled shapes from the outside inwards instead of // them as filled shapes from the outside inwards instead of
@ -244,12 +237,13 @@ AppMenuButton.prototype = {
_init: function() { _init: function() {
PanelMenu.Button.prototype._init.call(this, 0.0); PanelMenu.Button.prototype._init.call(this, 0.0);
this._metaDisplay = global.screen.get_display();
this._startingApps = []; this._startingApps = [];
this._targetApp = null; this._targetApp = null;
let bin = new St.Bin({ name: 'appMenu' }); let bin = new St.Bin({ name: 'appMenu' });
this.actor.add_actor(bin); this.actor.set_child(bin);
this.actor.reactive = false; this.actor.reactive = false;
this._targetIsCurrent = false; this._targetIsCurrent = false;
@ -293,9 +287,8 @@ AppMenuButton.prototype = {
this._spinner.actor.lower_bottom(); this._spinner.actor.lower_bottom();
let tracker = Shell.WindowTracker.get_default(); let tracker = Shell.WindowTracker.get_default();
let appSys = Shell.AppSystem.get_default();
tracker.connect('notify::focus-app', Lang.bind(this, this._sync)); tracker.connect('notify::focus-app', Lang.bind(this, this._sync));
appSys.connect('app-state-changed', Lang.bind(this, this._onAppStateChanged)); tracker.connect('app-state-changed', Lang.bind(this, this._onAppStateChanged));
global.window_manager.connect('switch-workspace', Lang.bind(this, this._sync)); global.window_manager.connect('switch-workspace', Lang.bind(this, this._sync));
@ -459,7 +452,7 @@ AppMenuButton.prototype = {
this._targetApp.request_quit(); this._targetApp.request_quit();
}, },
_onAppStateChanged: function(appSys, app) { _onAppStateChanged: function(tracker, app) {
let state = app.state; let state = app.state;
if (state != Shell.AppState.STARTING) { if (state != Shell.AppState.STARTING) {
this._startingApps = this._startingApps.filter(function(a) { this._startingApps = this._startingApps.filter(function(a) {
@ -564,10 +557,10 @@ ActivitiesButton.prototype = {
PanelMenu.Button.prototype._init.call(this, 0.0); PanelMenu.Button.prototype._init.call(this, 0.0);
let container = new Shell.GenericContainer(); let container = new Shell.GenericContainer();
container.connect('get-preferred-width', Lang.bind(this, this._containerGetPreferredWidth)); container.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth));
container.connect('get-preferred-height', Lang.bind(this, this._containerGetPreferredHeight)); container.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
container.connect('allocate', Lang.bind(this, this._containerAllocate)); container.connect('allocate', Lang.bind(this, this._allocate));
this.actor.add_actor(container); this.actor.child = container;
this.actor.name = 'panelActivities'; this.actor.name = 'panelActivities';
/* Translators: If there is no suitable word for "Activities" /* Translators: If there is no suitable word for "Activities"
@ -599,15 +592,15 @@ ActivitiesButton.prototype = {
this._xdndTimeOut = 0; this._xdndTimeOut = 0;
}, },
_containerGetPreferredWidth: function(actor, forHeight, alloc) { _getPreferredWidth: function(actor, forHeight, alloc) {
[alloc.min_size, alloc.natural_size] = this._label.get_preferred_width(forHeight); [alloc.min_size, alloc.natural_size] = this._label.get_preferred_width(forHeight);
}, },
_containerGetPreferredHeight: function(actor, forWidth, alloc) { _getPreferredHeight: function(actor, forWidth, alloc) {
[alloc.min_size, alloc.natural_size] = this._label.get_preferred_height(forWidth); [alloc.min_size, alloc.natural_size] = this._label.get_preferred_height(forWidth);
}, },
_containerAllocate: function(actor, box, flags) { _allocate: function(actor, box, flags) {
this._label.allocate(box, flags); this._label.allocate(box, flags);
// The hot corner needs to be outside any padding/alignment // The hot corner needs to be outside any padding/alignment
@ -705,110 +698,12 @@ function PanelCorner(panel, side) {
} }
PanelCorner.prototype = { PanelCorner.prototype = {
_init: function(box, side) { _init: function(panel, side) {
this._panel = panel;
this._side = 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 = 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('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() { _repaint: function() {
@ -871,14 +766,20 @@ PanelCorner.prototype = {
cr.restore(); cr.restore();
}, },
_styleChanged: function() { relayout: function() {
let node = this.actor.get_theme_node(); let node = this.actor.get_theme_node();
let cornerRadius = node.get_length("-panel-corner-radius"); let cornerRadius = node.get_length("-panel-corner-radius");
let innerBorderWidth = node.get_length('-panel-corner-inner-border-width'); let innerBorderWidth = node.get_length('-panel-corner-inner-border-width');
this.actor.set_size(cornerRadius, innerBorderWidth + cornerRadius); this.actor.set_size(cornerRadius,
this.actor.set_anchor_point(0, innerBorderWidth); 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);
} }
}; };
@ -889,8 +790,9 @@ function Panel() {
Panel.prototype = { Panel.prototype = {
_init : function() { _init : function() {
this.actor = new Shell.GenericContainer({ name: 'panel', this.actor = new St.BoxLayout({ style_class: 'menu-bar',
reactive: true }); name: 'panel',
reactive: true });
this.actor._delegate = this; this.actor._delegate = this;
this._statusArea = {}; this._statusArea = {};
@ -902,217 +804,259 @@ Panel.prototype = {
this.actor.remove_style_class_name('in-overview'); this.actor.remove_style_class_name('in-overview');
})); }));
this._leftPointerBarrier = 0;
this._rightPointerBarrier = 0;
this._menus = new PopupMenu.PopupMenuManager(this); this._menus = new PopupMenu.PopupMenuManager(this);
this._leftBox = new St.BoxLayout({ name: 'panelLeft' }); this._leftBox = new St.BoxLayout({ name: 'panelLeft' });
this.actor.add_actor(this._leftBox);
this._centerBox = new St.BoxLayout({ name: 'panelCenter' }); this._centerBox = new St.BoxLayout({ name: 'panelCenter' });
this.actor.add_actor(this._centerBox);
this._rightBox = new St.BoxLayout({ name: 'panelRight' }); this._rightBox = new St.BoxLayout({ name: 'panelRight' });
this.actor.add_actor(this._rightBox);
if (this.actor.get_direction() == St.TextDirection.RTL) this._leftCorner = new PanelCorner(this, St.Side.LEFT);
this._leftCorner = new PanelCorner(this._rightBox, St.Side.LEFT); this._rightCorner = new PanelCorner(this, St.Side.RIGHT);
else
this._leftCorner = new PanelCorner(this._leftBox, St.Side.LEFT);
this.actor.add_actor(this._leftCorner.actor); /* 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);
if (this.actor.get_direction() == St.TextDirection.RTL) let sideWidth, centerWidth;
this._rightCorner = new PanelCorner(this._leftBox, St.Side.RIGHT); centerWidth = centerNaturalWidth;
else sideWidth = (allocWidth - centerWidth) / 2;
this._rightCorner = new PanelCorner(this._rightBox, St.Side.RIGHT);
this.actor.add_actor(this._rightCorner.actor);
this.actor.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); let childBox = new Clutter.ActorBox();
this.actor.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight));
this.actor.connect('allocate', Lang.bind(this, this._allocate)); 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);
}));
/* Button on the left side of the panel. */ /* Button on the left side of the panel. */
if (global.session_type == Shell.SessionType.USER) { this._activitiesButton = new ActivitiesButton();
this._activitiesButton = new ActivitiesButton(); this._activities = this._activitiesButton.actor;
this._activities = this._activitiesButton.actor; this._leftBox.add(this._activities);
this._leftBox.add(this._activities);
// The activities button has a pretend menu, so as to integrate // The activities button has a pretend menu, so as to integrate
// more cleanly with the rest of the panel // more cleanly with the rest of the panel
this._menus.addMenu(this._activitiesButton.menu); this._menus.addMenu(this._activitiesButton.menu);
this._appMenu = new AppMenuButton(); // Synchronize the button's pseudo classes with its corner
this._leftBox.add(this._appMenu.actor); this._activities.connect('style-changed', Lang.bind(this,
this._menus.addMenu(this._appMenu.menu); 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);
/* center */ /* center */
if (global.session_type == Shell.SessionType.USER) this._dateMenu = new DateMenu.DateMenuButton();
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._centerBox.add(this._dateMenu.actor, { y_fill: true });
this._menus.addMenu(this._dateMenu.menu); this._menus.addMenu(this._dateMenu.menu);
/* right */ /* right */
if (global.session_type == Shell.SessionType.GDM) {
this._status_area_order = GDM_STATUS_AREA_ORDER; // System status applets live in statusBox, while legacy tray icons
this._status_area_shell_implementation = GDM_STATUS_AREA_SHELL_IMPLEMENTATION; // live in trayBox
} else { // The trayBox is hidden when there are no tray icons.
this._status_area_order = STANDARD_STATUS_AREA_ORDER; this._trayBox = new St.BoxLayout({ name: 'legacyTray' });
this._status_area_shell_implementation = STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION; 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);
}));
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.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here',
{ sortGroup: CtrlAltTab.SortGroup.TOP });
Main.layoutManager.connect('monitors-changed', Lang.bind(this, this._relayout));
this._relayout();
},
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];
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;
} }
Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded)); Main.statusIconDispatcher.connect('status-icon-added', Lang.bind(this, this._onTrayIconAdded));
Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved)); Main.statusIconDispatcher.connect('status-icon-removed', Lang.bind(this, this._onTrayIconRemoved));
Main.layoutManager.panelBox.add(this.actor); // PopupMenuManager depends on menus being added in order for
Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'start-here', // keyboard navigation, so we couldn't add this before
{ sortGroup: CtrlAltTab.SortGroup.TOP }); this._menus.addMenu(this._userMenu.menu);
}, },
_getPreferredWidth: function(actor, forHeight, alloc) { startupAnimation: function() {
alloc.min_size = -1; let oldY = this.actor.y;
alloc.natural_size = Main.layoutManager.primaryMonitor.width; 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'
});
}, },
_getPreferredHeight: function(actor, forWidth, alloc) { _relayout: function() {
// We don't need to implement this; it's forced by the CSS let primary = Main.layoutManager.primaryMonitor;
alloc.min_size = -1;
alloc.natural_size = -1; 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();
}, },
_allocate: function(actor, box, flags) { _onTrayIconAdded: function(o, icon, role) {
let allocWidth = box.x2 - box.x1; icon.height = PANEL_ICON_SIZE;
let allocHeight = box.y2 - box.y1;
let [leftMinWidth, leftNaturalWidth] = this._leftBox.get_preferred_width(-1); if (STANDARD_TRAY_ICON_SHELL_IMPLEMENTATION[role]) {
let [centerMinWidth, centerNaturalWidth] = this._centerBox.get_preferred_width(-1); // This icon is legacy, and replaced by a Shell version
let [rightMinWidth, rightNaturalWidth] = this._rightBox.get_preferred_width(-1); // Hide it
return;
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); // Figure out the index in our well-known order for this icon
let position = STANDARD_TRAY_ICON_ORDER.indexOf(role);
childBox.x1 = Math.ceil(sideWidth); icon._rolePosition = position;
childBox.y1 = 0; let children = this._trayBox.get_children();
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 < 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.addToStatusArea(role, indicator, i);
}
},
_insertStatusItem: function(actor, position) {
let children = this._rightBox.get_children();
let i; 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--) { for (i = children.length - 1; i >= 0; i--) {
let rolePosition = children[i]._rolePosition; let rolePosition = children[i]._rolePosition;
if (position > rolePosition) { if (!rolePosition || position > rolePosition) {
this._rightBox.insert_actor(actor, i + 1); this._trayBox.insert_actor(icon, i + 1);
break; break;
} }
} }
if (i == -1) { if (i == -1) {
// If we didn't find a position, we must be first // If we didn't find a position, we must be first
this._rightBox.insert_actor(actor, 0); this._trayBox.insert_actor(icon, 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;
} }
icon.height = PANEL_ICON_SIZE; // Make sure the trayBox is shown.
let buttonBox = new PanelMenu.ButtonBox(); this._trayBox.show();
let box = buttonBox.actor;
box.add_actor(icon);
this._insertStatusItem(box, this._status_area_order.indexOf(role));
}, },
_onTrayIconRemoved: function(o, icon) { _onTrayIconRemoved: function(o, icon) {
let box = icon.get_parent(); if (icon.get_parent() != null)
if (box && box._delegate instanceof PanelMenu.ButtonBox) this._trayBox.remove_actor(icon);
box.destroy();
}, },
}; };

View File

@ -2,117 +2,31 @@
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Gtk = imports.gi.Gtk; const Gtk = imports.gi.Gtk;
const Lang = imports.lang;
const Shell = imports.gi.Shell;
const Signals = imports.signals;
const St = imports.gi.St; const St = imports.gi.St;
const Main = imports.ui.main; const Lang = imports.lang;
const Params = imports.misc.params;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const Main = imports.ui.main;
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) { function Button(menuAlignment) {
this._init(menuAlignment); this._init(menuAlignment);
} }
Button.prototype = { Button.prototype = {
__proto__: ButtonBox.prototype,
_init: function(menuAlignment) { _init: function(menuAlignment) {
ButtonBox.prototype._init.call(this, { reactive: true, this.actor = new St.Bin({ style_class: 'panel-button',
can_focus: true, reactive: true,
track_hover: true }); can_focus: true,
x_fill: true,
y_fill: false,
track_hover: true });
this.actor._delegate = this;
this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress)); this.actor.connect('button-press-event', Lang.bind(this, this._onButtonPress));
this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress)); this.actor.connect('key-press-event', Lang.bind(this, this._onSourceKeyPress));
this.menu = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP); this.menu = new PopupMenu.PopupMenu(this.actor, menuAlignment, St.Side.TOP, 0);
this.menu.actor.add_style_class_name('panel-menu');
this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged)); this.menu.connect('open-state-changed', Lang.bind(this, this._onOpenStateChanged));
this.menu.actor.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress)); this.menu.actor.connect('key-press-event', Lang.bind(this, this._onMenuKeyPress));
Main.uiGroup.add_actor(this.menu.actor); Main.chrome.addActor(this.menu.actor, { affectsStruts: false });
this.menu.actor.hide(); this.menu.actor.hide();
}, },
@ -165,18 +79,8 @@ Button.prototype = {
this.actor.add_style_pseudo_class('active'); this.actor.add_style_pseudo_class('active');
else else
this.actor.remove_style_pseudo_class('active'); 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: /* SystemStatusButton:
* *
@ -196,8 +100,7 @@ SystemStatusButton.prototype = {
this._iconActor = new St.Icon({ icon_name: iconName, this._iconActor = new St.Icon({ icon_name: iconName,
icon_type: St.IconType.SYMBOLIC, icon_type: St.IconType.SYMBOLIC,
style_class: 'system-status-icon' }); style_class: 'system-status-icon' });
this.actor.add_actor(this._iconActor); this.actor.set_child(this._iconActor);
this.actor.add_style_class_name('panel-status-button');
this.setTooltip(tooltipText); this.setTooltip(tooltipText);
}, },

View File

@ -60,13 +60,13 @@ PlaceInfo.prototype = {
// Helper function to translate launch parameters into a GAppLaunchContext // Helper function to translate launch parameters into a GAppLaunchContext
function _makeLaunchContext(params) function _makeLaunchContext(params)
{ {
params = Params.parse(params, { workspace: -1, params = Params.parse(params, { workspace: null,
timestamp: 0 }); timestamp: null });
let launchContext = global.create_app_launch_context(); let launchContext = global.create_app_launch_context();
if (params.workspace != -1) if (params.workspace != null)
launchContext.set_desktop(params.workspace); launchContext.set_desktop(params.workspace.index());
if (params.timestamp != 0) if (params.timestamp != null)
launchContext.set_timestamp(params.timestamp); launchContext.set_timestamp(params.timestamp);
return launchContext; return launchContext;
@ -118,9 +118,9 @@ PlaceDeviceInfo.prototype = {
this._mount.unmount_finish(res); this._mount.unmount_finish(res);
} catch (e) { } catch (e) {
let message = _("Failed to unmount '%s'").format(o.get_name()); let message = _("Failed to unmount '%s'").format(o.get_name());
Main.overview.setMessage(message, Main.overview.shellInfo.setMessage(message,
Lang.bind(this, this.remove), Lang.bind(this, this.remove),
_("Retry")); _("Retry"));
} }
} }
}; };

View File

@ -23,10 +23,10 @@
const Lang = imports.lang; const Lang = imports.lang;
const Signals = imports.signals; const Signals = imports.signals;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const AccountsService = imports.gi.AccountsService;
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const St = imports.gi.St; const St = imports.gi.St;
const Pango = imports.gi.Pango; const Pango = imports.gi.Pango;
const Gdm = imports.gi.Gdm;
const Gio = imports.gi.Gio; const Gio = imports.gi.Gio;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const Polkit = imports.gi.Polkit; const Polkit = imports.gi.Polkit;
@ -92,7 +92,7 @@ AuthenticationDialog.prototype = {
let userName = userNames[0]; let userName = userNames[0];
this._user = AccountsService.UserManager.get_default().get_user(userName); this._user = Gdm.UserManager.ref_default().get_user(userName);
let userRealName = this._user.get_real_name() let userRealName = this._user.get_real_name()
this._userLoadedId = this._user.connect('notify::is_loaded', this._userLoadedId = this._user.connect('notify::is_loaded',
Lang.bind(this, this._onUserChanged)); Lang.bind(this, this._onUserChanged));
@ -142,7 +142,6 @@ AuthenticationDialog.prototype = {
this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate)); this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate));
this._passwordBox.add(this._passwordEntry, this._passwordBox.add(this._passwordEntry,
{expand: true }); {expand: true });
this.setInitialKeyFocus(this._passwordEntry);
this._passwordBox.hide(); this._passwordBox.hide();
this._errorMessageLabel = new St.Label({ style_class: 'polkit-dialog-error-label' }); this._errorMessageLabel = new St.Label({ style_class: 'polkit-dialog-error-label' });
@ -187,6 +186,13 @@ AuthenticationDialog.prototype = {
this._session.connect('request', Lang.bind(this, this._onSessionRequest)); this._session.connect('request', Lang.bind(this, this._onSessionRequest));
this._session.connect('show-error', Lang.bind(this, this._onSessionShowError)); this._session.connect('show-error', Lang.bind(this, this._onSessionShowError));
this._session.connect('show-info', Lang.bind(this, this._onSessionShowInfo)); 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() { startAuthentication: function() {

View File

@ -15,17 +15,6 @@ const Tweener = imports.ui.tweener;
const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */ 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) { function PopupBaseMenuItem(params) {
this._init(params); this._init(params);
} }
@ -35,7 +24,6 @@ PopupBaseMenuItem.prototype = {
params = Params.parse (params, { reactive: true, params = Params.parse (params, { reactive: true,
activate: true, activate: true,
hover: true, hover: true,
sensitive: true,
style_class: null style_class: null
}); });
this.actor = new Shell.GenericContainer({ style_class: 'popup-menu-item', this.actor = new Shell.GenericContainer({ style_class: 'popup-menu-item',
@ -53,15 +41,11 @@ PopupBaseMenuItem.prototype = {
this._columnWidths = null; this._columnWidths = null;
this._spacing = 0; this._spacing = 0;
this.active = false; this.active = false;
this._activatable = params.reactive && params.activate;
this.sensitive = this._activatable && params.sensitive;
this.setSensitive(this.sensitive);
if (params.style_class) if (params.style_class)
this.actor.add_style_class_name(params.style_class); this.actor.add_style_class_name(params.style_class);
if (this._activatable) { if (params.reactive && params.activate) {
this.actor.connect('button-release-event', Lang.bind(this, this._onButtonReleaseEvent)); this.actor.connect('button-release-event', Lang.bind(this, this._onButtonReleaseEvent));
this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent)); this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
} }
@ -122,23 +106,6 @@ 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() { destroy: function() {
this.actor.destroy(); this.actor.destroy();
this.emit('destroy'); this.emit('destroy');
@ -236,7 +203,7 @@ PopupBaseMenuItem.prototype = {
let child = this._children[i]; let child = this._children[i];
if (i > 0) if (i > 0)
width += this._spacing; width += this._spacing;
let [min, natural] = child.actor.get_preferred_width(-1); let [min, natural] = child.actor.get_preferred_width(forHeight);
width += natural; width += natural;
} }
} }
@ -244,25 +211,10 @@ PopupBaseMenuItem.prototype = {
}, },
_getPreferredHeight: function(actor, forWidth, alloc) { _getPreferredHeight: function(actor, forWidth, alloc) {
let height = 0, x = 0, minWidth, childWidth; let height = 0;
for (let i = 0; i < this._children.length; i++) { for (let i = 0; i < this._children.length; i++) {
let child = this._children[i]; let child = this._children[i];
if (this._columnWidths) { let [min, natural] = child.actor.get_preferred_height(-1);
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) if (natural > height)
height = natural; height = natural;
} }
@ -320,14 +272,7 @@ PopupBaseMenuItem.prototype = {
} }
extraWidth = availWidth - naturalWidth; extraWidth = availWidth - naturalWidth;
} else { } else {
if (child.span == -1) { availWidth = naturalWidth;
if (direction == St.TextDirection.LTR)
availWidth = box.x2 - x;
else
availWidth = x - box.x1;
} else {
availWidth = naturalWidth;
}
extraWidth = 0; extraWidth = 0;
} }
@ -335,7 +280,7 @@ PopupBaseMenuItem.prototype = {
if (child.expand) { if (child.expand) {
childBox.x1 = x; childBox.x1 = x;
childBox.x2 = x + availWidth; childBox.x2 = x + availWidth;
} else if (child.align === St.Align.MIDDLE) { } else if (child.align === St.Align.CENTER) {
childBox.x1 = x + Math.round(extraWidth / 2); childBox.x1 = x + Math.round(extraWidth / 2);
childBox.x2 = childBox.x1 + naturalWidth; childBox.x2 = childBox.x1 + naturalWidth;
} else if (child.align === St.Align.END) { } else if (child.align === St.Align.END) {
@ -349,7 +294,7 @@ PopupBaseMenuItem.prototype = {
if (child.expand) { if (child.expand) {
childBox.x1 = x - availWidth; childBox.x1 = x - availWidth;
childBox.x2 = x; childBox.x2 = x;
} else if (child.align === St.Align.MIDDLE) { } else if (child.align === St.Align.CENTER) {
childBox.x1 = x - Math.round(extraWidth / 2); childBox.x1 = x - Math.round(extraWidth / 2);
childBox.x2 = childBox.x1 + naturalWidth; childBox.x2 = childBox.x1 + naturalWidth;
} else if (child.align === St.Align.END) { } else if (child.align === St.Align.END) {
@ -363,7 +308,7 @@ PopupBaseMenuItem.prototype = {
} }
} }
let [minHeight, naturalHeight] = child.actor.get_preferred_height(childBox.x2 - childBox.x1); let [minHeight, naturalHeight] = child.actor.get_preferred_height(-1);
childBox.y1 = Math.round(box.y1 + (height - naturalHeight) / 2); childBox.y1 = Math.round(box.y1 + (height - naturalHeight) / 2);
childBox.y2 = childBox.y1 + naturalHeight; childBox.y2 = childBox.y1 + naturalHeight;
@ -761,8 +706,7 @@ PopupSwitchMenuItem.prototype = {
this.addActor(this.label); this.addActor(this.label);
this._statusBin = new St.Bin({ x_align: St.Align.END }); this._statusBin = new St.Bin({ x_align: St.Align.END });
this.addActor(this._statusBin, this.addActor(this._statusBin, { align: St.Align.END });
{ expand: true, span: -1, align: St.Align.END });
this._statusLabel = new St.Label({ text: '', this._statusLabel = new St.Label({ text: '',
style_class: 'popup-inactive-menu-item' style_class: 'popup-inactive-menu-item'
@ -856,58 +800,14 @@ PopupMenuBase.prototype = {
this.passEvents = false; this.passEvents = false;
this._activeMenuItem = null; this._activeMenuItem = null;
this._childMenus = [];
}, },
addAction: function(title, callback) { addAction: function(title, callback) {
let menuItem = new PopupMenuItem(title); var menuItem = new PopupMenuItem(title);
this.addMenuItem(menuItem); this.addMenuItem(menuItem);
menuItem.connect('activate', Lang.bind(this, function (menuItem, event) { menuItem.connect('activate', Lang.bind(this, function (menuItem, event) {
callback(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);
}, },
/** /**
@ -943,17 +843,6 @@ PopupMenuBase.prototype = {
this.emit('active-changed', null); 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) { menuItem._activateId = menuItem.connect('activate', Lang.bind(this, function (menuItem, event) {
this.emit('activate', menuItem); this.emit('activate', menuItem);
this.close(true); this.close(true);
@ -961,7 +850,6 @@ PopupMenuBase.prototype = {
menuItem.connect('destroy', Lang.bind(this, function(emitter) { menuItem.connect('destroy', Lang.bind(this, function(emitter) {
menuItem.disconnect(menuItem._activateId); menuItem.disconnect(menuItem._activateId);
menuItem.disconnect(menuItem._activeChangeId); menuItem.disconnect(menuItem._activeChangeId);
menuItem.disconnect(menuItem._sensitiveChangeId);
if (menuItem.menu) { if (menuItem.menu) {
menuItem.menu.disconnect(menuItem._subMenuActivateId); menuItem.menu.disconnect(menuItem._subMenuActivateId);
menuItem.menu.disconnect(menuItem._subMenuActiveChangeId); menuItem.menu.disconnect(menuItem._subMenuActiveChangeId);
@ -972,39 +860,6 @@ 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) { addMenuItem: function(menuItem, position) {
let before_item = null; let before_item = null;
if (position == undefined) { if (position == undefined) {
@ -1036,14 +891,6 @@ PopupMenuBase.prototype = {
if (!open) if (!open)
menuItem.menu.close(false); 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) } else if (menuItem instanceof PopupBaseMenuItem)
this._connectItemSignals(menuItem); this._connectItemSignals(menuItem);
else else
@ -1107,10 +954,6 @@ PopupMenuBase.prototype = {
return null; return null;
}, },
get numMenuItems() {
return this._getMenuItems().length;
},
removeAll: function() { removeAll: function() {
let children = this._getMenuItems(); let children = this._getMenuItems();
for (let i = 0; i < children.length; i++) { for (let i = 0; i < children.length; i++) {
@ -1142,11 +985,12 @@ function PopupMenu() {
PopupMenu.prototype = { PopupMenu.prototype = {
__proto__: PopupMenuBase.prototype, __proto__: PopupMenuBase.prototype,
_init: function(sourceActor, alignment, arrowSide) { _init: function(sourceActor, alignment, arrowSide, gap) {
PopupMenuBase.prototype._init.call (this, sourceActor, 'popup-menu-content'); PopupMenuBase.prototype._init.call (this, sourceActor, 'popup-menu-content');
this._alignment = alignment; this._alignment = alignment;
this._arrowSide = arrowSide; this._arrowSide = arrowSide;
this._gap = gap;
this._boxPointer = new BoxPointer.BoxPointer(arrowSide, this._boxPointer = new BoxPointer.BoxPointer(arrowSide,
{ x_fill: true, { x_fill: true,
@ -1204,11 +1048,9 @@ PopupMenu.prototype = {
this.isOpen = true; this.isOpen = true;
this._boxPointer.setPosition(this.sourceActor, this._alignment); this._boxPointer.setPosition(this.sourceActor, this._gap, this._alignment);
this._boxPointer.show(animate); this._boxPointer.show(animate);
this.actor.raise_top();
this.emit('open-state-changed', true); this.emit('open-state-changed', true);
}, },
@ -1476,227 +1318,6 @@ 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. /* Basic implementation of a menu manager.
* Call addMenu to add menus * Call addMenu to add menus
@ -1716,7 +1337,6 @@ PopupMenuManager.prototype = {
this._keyFocusNotifyId = 0; this._keyFocusNotifyId = 0;
this._activeMenu = null; this._activeMenu = null;
this._menus = []; this._menus = [];
this._menuStack = [];
this._preGrabInputMode = null; this._preGrabInputMode = null;
this._grabbedFromKeynav = false; this._grabbedFromKeynav = false;
}, },
@ -1725,8 +1345,6 @@ PopupMenuManager.prototype = {
let menudata = { let menudata = {
menu: menu, menu: menu,
openStateChangeId: menu.connect('open-state-changed', Lang.bind(this, this._onMenuOpenState)), 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)), destroyId: menu.connect('destroy', Lang.bind(this, this._onMenuDestroy)),
enterId: 0, enterId: 0,
focusInId: 0 focusInId: 0
@ -1754,8 +1372,6 @@ PopupMenuManager.prototype = {
let menudata = this._menus[position]; let menudata = this._menus[position];
menu.disconnect(menudata.openStateChangeId); menu.disconnect(menudata.openStateChangeId);
menu.disconnect(menudata.childMenuAddedId);
menu.disconnect(menudata.childMenuRemovedId);
menu.disconnect(menudata.destroyId); menu.disconnect(menudata.destroyId);
if (menudata.enterId) if (menudata.enterId)
@ -1793,20 +1409,8 @@ PopupMenuManager.prototype = {
}, },
_onMenuOpenState: function(menu, open) { _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; 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 // Check what the focus was before calling pushModal/popModal
let focus = global.stage.key_focus; let focus = global.stage.key_focus;
@ -1839,14 +1443,6 @@ PopupMenuManager.prototype = {
} }
}, },
_onChildMenuAdded: function(menu, childMenu) {
this.addMenu(childMenu);
},
_onChildMenuRemoved: function(menu, childMenu) {
this.removeMenu(childMenu);
},
// change the currently-open menu without dropping grab // change the currently-open menu without dropping grab
_changeMenu: function(newMenu) { _changeMenu: function(newMenu) {
if (this._activeMenu) { if (this._activeMenu) {
@ -1855,8 +1451,6 @@ PopupMenuManager.prototype = {
// before closing it to keep that from happening // before closing it to keep that from happening
let oldMenu = this._activeMenu; let oldMenu = this._activeMenu;
this._activeMenu = null; this._activeMenu = null;
for (let i = this._menuStack.length - 1; i >= 0; i--)
this._menuStack[i].close(false);
oldMenu.close(false); oldMenu.close(false);
newMenu.open(false); newMenu.open(false);
} else } else
@ -1867,15 +1461,6 @@ PopupMenuManager.prototype = {
if (!this.grabbed || menu == this._activeMenu) if (!this.grabbed || menu == this._activeMenu)
return false; 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); this._changeMenu(menu);
return false; return false;
}, },
@ -1888,8 +1473,6 @@ PopupMenuManager.prototype = {
if (focus) { if (focus) {
if (this._activeMenuContains(focus)) if (this._activeMenuContains(focus))
return; return;
if (this._menuStack.length > 0)
return;
if (focus._delegate && focus._delegate.menu && if (focus._delegate && focus._delegate.menu &&
this._findMenu(focus._delegate.menu) != -1) this._findMenu(focus._delegate.menu) != -1)
return; return;
@ -1948,11 +1531,6 @@ PopupMenuManager.prototype = {
if (this._activeMenu != null && this._activeMenu.passEvents) if (this._activeMenu != null && this._activeMenu.passEvents)
return false; return false;
if (this._didPop) {
this._didPop = false;
return true;
}
let activeMenuContains = this._eventIsOnActiveMenu(event); let activeMenuContains = this._eventIsOnActiveMenu(event);
let eventType = event.type(); let eventType = event.type();

View File

@ -112,43 +112,6 @@ function SearchProvider(title) {
SearchProvider.prototype = { SearchProvider.prototype = {
_init: function(title) { _init: function(title) {
this.title = 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);
}, },
/** /**
@ -352,18 +315,9 @@ SearchSystem.prototype = {
}, },
registerProvider: function (provider) { registerProvider: function (provider) {
provider.searchSystem = this;
this._providers.push(provider); this._providers.push(provider);
}, },
unregisterProvider: function (provider) {
let index = this._providers.indexOf(provider);
if (index == -1)
return;
provider.searchSystem = null;
this._providers.splice(index, 1);
},
getProviders: function() { getProviders: function() {
return this._providers; return this._providers;
}, },
@ -377,23 +331,12 @@ SearchSystem.prototype = {
this._previousResults = []; this._previousResults = [];
}, },
addProviderItems: function(provider, items) {
this.emit('search-updated', provider, items);
},
updateSearch: function(searchString) { updateSearch: function(searchString) {
searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, ''); searchString = searchString.replace(/^\s+/g, '').replace(/\s+$/g, '');
if (searchString == '') if (searchString == '')
return; return [];
let terms = searchString.split(/\s+/); let terms = searchString.split(/\s+/);
this.updateSearchResults(terms);
},
updateSearchResults: function(terms) {
if (!terms)
return;
let isSubSearch = terms.length == this._previousTerms.length; let isSubSearch = terms.length == this._previousTerms.length;
if (isSubSearch) { if (isSubSearch) {
for (let i = 0; i < terms.length; i++) { for (let i = 0; i < terms.length; i++) {
@ -406,12 +349,12 @@ SearchSystem.prototype = {
let results = []; let results = [];
if (isSubSearch) { if (isSubSearch) {
for (let i = 0; i < this._providers.length; i++) { for (let i = 0; i < this._previousResults.length; i++) {
let [provider, previousResults] = this._previousResults[i]; let [provider, previousResults] = this._previousResults[i];
provider.tryCancelAsync();
try { try {
let providerResults = provider.getSubsearchResultSet(previousResults, terms); let providerResults = provider.getSubsearchResultSet(previousResults, terms);
results.push([provider, providerResults]); if (providerResults.length > 0)
results.push([provider, providerResults]);
} catch (error) { } catch (error) {
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message); global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
} }
@ -419,10 +362,10 @@ SearchSystem.prototype = {
} else { } else {
for (let i = 0; i < this._providers.length; i++) { for (let i = 0; i < this._providers.length; i++) {
let provider = this._providers[i]; let provider = this._providers[i];
provider.tryCancelAsync();
try { try {
let providerResults = provider.getInitialResultSet(terms); let providerResults = provider.getInitialResultSet(terms);
results.push([provider, providerResults]); if (providerResults.length > 0)
results.push([provider, providerResults]);
} catch (error) { } catch (error) {
global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message); global.log ('A ' + error.name + ' has occured in ' + provider.title + ': ' + error.message);
} }
@ -431,7 +374,8 @@ SearchSystem.prototype = {
this._previousTerms = terms; this._previousTerms = terms;
this._previousResults = results; this._previousResults = results;
this.emit('search-completed', results);
}, return results;
}
}; };
Signals.addSignalMethods(SearchSystem.prototype); Signals.addSignalMethods(SearchSystem.prototype);

View File

@ -88,7 +88,7 @@ SearchResult.prototype = {
}, },
getDragActor: function(stageX, stageY) { getDragActor: function(stageX, stageY) {
return this.metaInfo['createIcon'](Main.overview.dashIconSize); return this.metaInfo['createIcon'](Main.overview.dash.iconSize);
}, },
shellWorkspaceLaunch: function(params) { shellWorkspaceLaunch: function(params) {
@ -100,17 +100,17 @@ SearchResult.prototype = {
}; };
function GridSearchResults(provider, grid) { function GridSearchResults(provider) {
this._init(provider, grid); this._init(provider);
} }
GridSearchResults.prototype = { GridSearchResults.prototype = {
__proto__: Search.SearchResultDisplay.prototype, __proto__: Search.SearchResultDisplay.prototype,
_init: function(provider, grid) { _init: function(provider) {
Search.SearchResultDisplay.prototype._init.call(this, provider); Search.SearchResultDisplay.prototype._init.call(this, provider);
this._grid = grid || new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS, this._grid = new IconGrid.IconGrid({ rowLimit: MAX_SEARCH_RESULTS_ROWS,
xAlign: St.Align.START }); xAlign: St.Align.START });
this.actor = new St.Bin({ x_align: St.Align.START }); this.actor = new St.Bin({ x_align: St.Align.START });
this.actor.set_child(this._grid.actor); this.actor.set_child(this._grid.actor);
@ -189,8 +189,6 @@ function SearchResults(searchSystem, openSearchSystem) {
SearchResults.prototype = { SearchResults.prototype = {
_init: function(searchSystem, openSearchSystem) { _init: function(searchSystem, openSearchSystem) {
this._searchSystem = searchSystem; 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._openSearchSystem = openSearchSystem;
this.actor = new St.BoxLayout({ name: 'searchResults', this.actor = new St.BoxLayout({ name: 'searchResults',
@ -225,11 +223,9 @@ SearchResults.prototype = {
this._selectedProvider = -1; this._selectedProvider = -1;
this._providers = this._searchSystem.getProviders(); this._providers = this._searchSystem.getProviders();
this._providerMeta = []; this._providerMeta = [];
this._providerMetaResults = {}; for (let i = 0; i < this._providers.length; i++)
for (let i = 0; i < this._providers.length; i++) {
this.createProviderMeta(this._providers[i]); this.createProviderMeta(this._providers[i]);
this._providerMetaResults[this.providers[i].title] = [];
}
this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' }); this._searchProvidersBox = new St.BoxLayout({ style_class: 'search-providers-box' });
this.actor.add(this._searchProvidersBox); this.actor.add(this._searchProvidersBox);
@ -294,23 +290,11 @@ SearchResults.prototype = {
} }
resultDisplayBin.set_child(resultDisplay.actor); resultDisplayBin.set_child(resultDisplay.actor);
this._providerMeta.push({ provider: provider, this._providerMeta.push({ actor: providerBox,
actor: providerBox,
resultDisplay: resultDisplay }); resultDisplay: resultDisplay });
this._content.add(providerBox); 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() { _clearDisplay: function() {
this._selectedProvider = -1; this._selectedProvider = -1;
this._visibleResultsCount = 0; this._visibleResultsCount = 0;
@ -321,12 +305,6 @@ SearchResults.prototype = {
} }
}, },
_clearDisplayForProvider: function(index) {
let meta = this._providerMeta[index];
meta.resultDisplay.clear();
meta.actor.hide();
},
reset: function() { reset: function() {
this._searchSystem.reset(); this._searchSystem.reset();
this._statusText.hide(); this._statusText.hide();
@ -341,24 +319,15 @@ SearchResults.prototype = {
this._statusText.show(); this._statusText.show();
}, },
doSearch: function (searchString) {
this._searchSystem.updateSearch(searchString);
},
_metaForProvider: function(provider) { _metaForProvider: function(provider) {
return this._providerMeta[this._providers.indexOf(provider)]; return this._providerMeta[this._providers.indexOf(provider)];
}, },
_updateCurrentResults: function(searchSystem, provider, results) { updateSearch: function (searchString) {
let terms = searchSystem.getTerms(); let results = this._searchSystem.updateSearch(searchString);
let meta = this._metaForProvider(provider);
meta.resultDisplay.clear(); this._clearDisplay();
meta.actor.show();
meta.resultDisplay.renderResults(results, terms);
return true;
},
_updateResults: function(searchSystem, results) {
if (results.length == 0) { if (results.length == 0) {
this._statusText.set_text(_("No matching results.")); this._statusText.set_text(_("No matching results."));
this._statusText.show(); this._statusText.show();
@ -368,7 +337,7 @@ SearchResults.prototype = {
this._statusText.hide(); this._statusText.hide();
} }
let terms = searchSystem.getTerms(); let terms = this._searchSystem.getTerms();
this._openSearchSystem.setSearchTerms(terms); this._openSearchSystem.setSearchTerms(terms);
// To avoid CSS transitions causing flickering // To avoid CSS transitions causing flickering
@ -380,15 +349,9 @@ SearchResults.prototype = {
for (let i = 0; i < results.length; i++) { for (let i = 0; i < results.length; i++) {
let [provider, providerResults] = results[i]; let [provider, providerResults] = results[i];
if (providerResults.length == 0) { let meta = this._metaForProvider(provider);
this._clearDisplayForProvider(i); meta.actor.show();
} else { meta.resultDisplay.renderResults(providerResults, terms);
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) if (this._selectedOpenSearchButton == -1)

View File

@ -1,10 +1,7 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const DBus = imports.dbus; const DBus = imports.dbus;
const Lang = imports.lang;
const Config = imports.misc.config;
const ExtensionSystem = imports.ui.extensionSystem;
const Main = imports.ui.main; const Main = imports.ui.main;
const GnomeShellIface = { const GnomeShellIface = {
@ -12,59 +9,12 @@ const GnomeShellIface = {
methods: [{ name: 'Eval', methods: [{ name: 'Eval',
inSignature: 's', inSignature: 's',
outSignature: 'bs' 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: [{ name: 'ExtensionStatusChanged', signals: [],
inSignature: 'sis' }],
properties: [{ name: 'OverviewActive', properties: [{ name: 'OverviewActive',
signature: 'b', signature: 'b',
access: 'readwrite' }, access: 'readwrite' }]
{ name: 'ApiVersion',
signature: 'i',
access: 'read' },
{ name: 'ShellVersion',
signature: 's',
access: 'read' }]
}; };
function GnomeShell() { function GnomeShell() {
@ -74,8 +24,6 @@ function GnomeShell() {
GnomeShell.prototype = { GnomeShell.prototype = {
_init: function() { _init: function() {
DBus.session.exportObject('/org/gnome/Shell', this); DBus.session.exportObject('/org/gnome/Shell', this);
ExtensionSystem.connect('extension-state-changed',
Lang.bind(this, this._extensionStateChanged));
}, },
/** /**
@ -108,84 +56,6 @@ GnomeShell.prototype = {
return [success, returnValue]; 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() { get OverviewActive() {
return Main.overview.visible; return Main.overview.visible;
}, },
@ -195,17 +65,6 @@ GnomeShell.prototype = {
Main.overview.show(); Main.overview.show();
else else
Main.overview.hide(); 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() { _onClicked: function() {
this.emit('activate'); this.emit('activate');
this._app.activate(); this._app.activate(-1);
} }
}; };
Signals.addSignalMethods(ListItem.prototype); Signals.addSignalMethods(ListItem.prototype);
@ -402,4 +402,4 @@ ShellProcessesDialog.prototype = {
_setButtonsForChoices(this, choices); _setButtonsForChoices(this, choices);
} }
} }
Signals.addSignalMethods(ShellProcessesDialog.prototype); Signals.addSignalMethods(ShellProcessesDialog.prototype);

View File

@ -40,6 +40,8 @@ const KEY_TEXT_SCALING_FACTOR = 'text-scaling-factor';
const HIGH_CONTRAST_THEME = 'HighContrast'; const HIGH_CONTRAST_THEME = 'HighContrast';
const KEYBOARD_SCHEMA = 'org.gnome.shell.keyboard'
function ATIndicator() { function ATIndicator() {
this._init.apply(this, arguments); this._init.apply(this, arguments);
} }
@ -68,8 +70,8 @@ ATIndicator.prototype = {
// 'screen-reader-enabled'); // 'screen-reader-enabled');
// this.menu.addMenuItem(screenReader); // this.menu.addMenuItem(screenReader);
let screenKeyboard = this._buildItem(_("Screen Keyboard"), APPLICATIONS_SCHEMA, let screenKeyboard = this._buildItem(_("Screen Keyboard"), KEYBOARD_SCHEMA,
'screen-keyboard-enabled'); 'show-keyboard');
this.menu.addMenuItem(screenKeyboard); this.menu.addMenuItem(screenKeyboard);
let visualBell = this._buildItemGConf(_("Visual Alerts"), client, KEY_VISUAL_BELL); let visualBell = this._buildItemGConf(_("Visual Alerts"), client, KEY_VISUAL_BELL);
@ -88,7 +90,11 @@ ATIndicator.prototype = {
this.menu.addMenuItem(mouseKeys); this.menu.addMenuItem(mouseKeys);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Universal Access Settings"), 'gnome-universal-access-panel.desktop'); 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);
});
}, },
_buildItemExtended: function(string, initial_value, writable, on_set) { _buildItemExtended: function(string, initial_value, writable, on_set) {

View File

@ -67,6 +67,7 @@ Indicator.prototype = {
new PopupMenu.PopupMenuItem(_("Set up a New Device...")), new PopupMenu.PopupMenuItem(_("Set up a New Device...")),
new PopupMenu.PopupSeparatorMenuItem()]; new PopupMenu.PopupSeparatorMenuItem()];
this._hasDevices = false; this._hasDevices = false;
this._deviceSep = this._fullMenuItems[0]; // hidden if no device exists
this._fullMenuItems[1].connect('activate', function() { this._fullMenuItems[1].connect('activate', function() {
GLib.spawn_command_line_async('bluetooth-sendto'); GLib.spawn_command_line_async('bluetooth-sendto');
@ -88,7 +89,11 @@ Indicator.prototype = {
this._applet.connect('notify::show-full-menu', Lang.bind(this, this._updateFullMenu)); this._applet.connect('notify::show-full-menu', Lang.bind(this, this._updateFullMenu));
this._updateFullMenu(); this._updateFullMenu();
this.menu.addSettingsAction(_("Bluetooth Settings"), 'bluetooth-properties.desktop'); this.menu.addAction(_("Bluetooth Settings"), function() {
Main.overview.hide()
let app = Shell.AppSystem.get_default().get_app('bluetooth-properties.desktop');
app.activate(-1);
});
this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest)); this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest)); this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
@ -157,6 +162,10 @@ Indicator.prototype = {
this._hasDevices = true; this._hasDevices = true;
} }
} }
if (this._hasDevices)
this._deviceSep.actor.show();
else
this._deviceSep.actor.hide();
}, },
_updateDeviceItem: function(item, device) { _updateDeviceItem: function(item, device) {
@ -268,15 +277,21 @@ Indicator.prototype = {
switch (device.type) { switch (device.type) {
case GnomeBluetoothApplet.Type.KEYBOARD: case GnomeBluetoothApplet.Type.KEYBOARD:
item.menu.addSettingsAction(_("Keyboard Settings"), 'gnome-keyboard-panel.desktop'); item.menu.addAction(_("Keyboard Settings"), function() {
GLib.spawn_command_line_async('gnome-control-center keyboard');
});
break; break;
case GnomeBluetoothApplet.Type.MOUSE: case GnomeBluetoothApplet.Type.MOUSE:
item.menu.addSettingsAction(_("Mouse Settings"), 'gnome-mouse-panel.desktop'); item.menu.addAction(_("Mouse Settings"), function() {
GLib.spawn_command_line_async('gnome-control-center mouse');
});
break; break;
case GnomeBluetoothApplet.Type.HEADSET: case GnomeBluetoothApplet.Type.HEADSET:
case GnomeBluetoothApplet.Type.HEADPHONES: case GnomeBluetoothApplet.Type.HEADPHONES:
case GnomeBluetoothApplet.Type.OTHER_AUDIO: case GnomeBluetoothApplet.Type.OTHER_AUDIO:
item.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop'); item.menu.addAction(_("Sound Settings"), function() {
GLib.spawn_command_line_async('gnome-control-center sound');
});
break; break;
default: default:
break; break;
@ -288,6 +303,8 @@ Indicator.prototype = {
this._showAll(this._fullMenuItems); this._showAll(this._fullMenuItems);
if (this._hasDevices) if (this._hasDevices)
this._showAll(this._deviceItems); this._showAll(this._deviceItems);
else
this._deviceSep.actor.hide();
} else { } else {
this._hideAll(this._fullMenuItems); this._hideAll(this._fullMenuItems);
this._hideAll(this._deviceItems); this._hideAll(this._deviceItems);

View File

@ -49,11 +49,10 @@ XKBIndicator.prototype = {
PanelMenu.Button.prototype._init.call(this, St.Align.START); PanelMenu.Button.prototype._init.call(this, St.Align.START);
this._container = new Shell.GenericContainer(); this._container = new Shell.GenericContainer();
this._container.connect('get-preferred-width', Lang.bind(this, this._containerGetPreferredWidth)); this._container.connect('get-preferred-width', Lang.bind(this, this._get_preferred_width));
this._container.connect('get-preferred-height', Lang.bind(this, this._containerGetPreferredHeight)); this._container.connect('get-preferred-height', Lang.bind(this, this._get_preferred_height));
this._container.connect('allocate', Lang.bind(this, this._containerAllocate)); this._container.connect('allocate', Lang.bind(this, this._allocate));
this.actor.add_actor(this._container); this.actor.set_child(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._iconActor = new St.Icon({ icon_name: 'keyboard', icon_type: St.IconType.SYMBOLIC, style_class: 'system-status-icon' });
this._container.add_actor(this._iconActor); this._container.add_actor(this._iconActor);
@ -62,45 +61,25 @@ XKBIndicator.prototype = {
this._showFlags = false; this._showFlags = false;
this._config = Gkbd.Configuration.get(); this._config = Gkbd.Configuration.get();
this._config.connect('changed', Lang.bind(this, this._syncConfig)); this._config.connect('changed', Lang.bind(this, this._sync_config));
this._config.connect('group-changed', Lang.bind(this, this._syncGroup)); this._config.connect('group-changed', Lang.bind(this, this._sync_group));
this._config.start_listen(); this._config.start_listen();
this._syncConfig(); this._sync_config();
if (global.session_type == Shell.SessionType.USER) { this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.menu.addAction(_("Show Keyboard Layout..."), Lang.bind(this, function() {
this.menu.addAction(_("Show Keyboard Layout"), Lang.bind(this, function() { Main.overview.hide();
Main.overview.hide(); Util.spawn(['gkbd-keyboard-display', '-g', String(this._config.get_current_group() + 1)]);
Util.spawn(['gkbd-keyboard-display', '-g', String(this._config.get_current_group() + 1)]); }));
})); this.menu.addAction(_("Localization Settings"), function() {
} Main.overview.hide();
this.menu.addSettingsAction(_("Region and Language Settings"), 'gnome-region-panel.desktop'); let app = Shell.AppSystem.get_default().get_app('gnome-region-panel.desktop');
app.activate(-1);
});
}, },
_adjustGroupNames: function(names) { _sync_config: function() {
// 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(); this._showFlags = this._config.if_flags_shown();
if (this._showFlags) { if (this._showFlags) {
this._container.set_skip_paint(this._iconActor, false); this._container.set_skip_paint(this._iconActor, false);
@ -122,7 +101,7 @@ XKBIndicator.prototype = {
for (let i = 0; i < this._labelActors.length; i++) for (let i = 0; i < this._labelActors.length; i++)
this._labelActors[i].destroy(); this._labelActors[i].destroy();
let short_names = this._adjustGroupNames(this._config.get_short_group_names()); let short_names = this._config.get_short_group_names();
this._selectedLayout = null; this._selectedLayout = null;
this._layoutItems = [ ]; this._layoutItems = [ ];
@ -147,10 +126,10 @@ XKBIndicator.prototype = {
this._container.set_skip_paint(shortLabel, true); this._container.set_skip_paint(shortLabel, true);
} }
this._syncGroup(); this._sync_group();
}, },
_syncGroup: function() { _sync_group: function() {
let selected = this._config.get_current_group(); let selected = this._config.get_current_group();
if (this._selectedLayout) { if (this._selectedLayout) {
@ -173,10 +152,10 @@ XKBIndicator.prototype = {
this._selectedLayout = item; this._selectedLayout = item;
}, },
_containerGetPreferredWidth: function(container, for_height, alloc) { _get_preferred_width: function(container, for_height, alloc) {
// Here, and in _containerGetPreferredHeight, we need to query /* Here, and in _get_preferred_height, we need to query for the
// for the height of all children, but we ignore the results height of all children, but we ignore the results for those
// for those we don't actually display. we don't actually display. */
let max_min_width = 0, max_natural_width = 0; let max_min_width = 0, max_natural_width = 0;
if (this._showFlags) if (this._showFlags)
[max_min_width, max_natural_width] = this._iconActor.get_preferred_width(for_height); [max_min_width, max_natural_width] = this._iconActor.get_preferred_width(for_height);
@ -193,7 +172,7 @@ XKBIndicator.prototype = {
alloc.natural_size = max_natural_width; alloc.natural_size = max_natural_width;
}, },
_containerGetPreferredHeight: function(container, for_width, alloc) { _get_preferred_height: function(container, for_width, alloc) {
let max_min_height = 0, max_natural_height = 0; let max_min_height = 0, max_natural_height = 0;
if (this._showFlags) if (this._showFlags)
[max_min_height, max_natural_height] = this._iconActor.get_preferred_height(for_width); [max_min_height, max_natural_height] = this._iconActor.get_preferred_height(for_width);
@ -210,7 +189,7 @@ XKBIndicator.prototype = {
alloc.natural_size = max_natural_height; alloc.natural_size = max_natural_height;
}, },
_containerAllocate: function(container, box, flags) { _allocate: function(container, box, flags) {
// translate box to (0, 0) // translate box to (0, 0)
box.x2 -= box.x1; box.x2 -= box.x1;
box.x1 = 0; box.x1 = 0;

View File

@ -45,6 +45,17 @@ const NM80211ApSecurityFlags = NetworkManager['80211ApSecurityFlags'];
// (the remaining are placed into More...) // (the remaining are placed into More...)
const NUM_VISIBLE_NETWORKS = 5; 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) { function macToArray(string) {
return string.split(':').map(function(el) { return string.split(':').map(function(el) {
return parseInt(el, 16); return parseInt(el, 16);
@ -91,13 +102,6 @@ function sortAccessPoints(accessPoints) {
}); });
} }
function ssidToLabel(ssid) {
let label = NetworkManager.utils_ssid_to_utf8(ssid);
if (!label)
label = _("<unknown>");
return label;
}
function NMNetworkMenuItem() { function NMNetworkMenuItem() {
this._init.apply(this, arguments); this._init.apply(this, arguments);
} }
@ -113,7 +117,10 @@ NMNetworkMenuItem.prototype = {
if (!title) { if (!title) {
let ssid = this.bestAP.get_ssid(); let ssid = this.bestAP.get_ssid();
title = ssidToLabel(ssid); if (ssid)
title = NetworkManager.utils_ssid_to_utf8(ssid);
if (!title)
title = _("<unknown>");
} }
this._label = new St.Label({ text: title }); this._label = new St.Label({ text: title });
@ -624,8 +631,15 @@ NMDevice.prototype = {
this.emit('network-lost'); this.emit('network-lost');
} }
if (newstate == NetworkManager.DeviceState.FAILED) { 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:
this.emit('activation-failed', reason); this.emit('activation-failed', reason);
break;
} }
this._updateStatusItem(); this._updateStatusItem();
@ -660,7 +674,7 @@ NMDevice.prototype = {
let dev_product = this.device.get_product(); let dev_product = this.device.get_product();
let dev_vendor = this.device.get_vendor(); let dev_vendor = this.device.get_vendor();
if (!dev_product || !dev_vendor) if (!dev_product || !dev_vendor)
return ''; return null;
let product = Util.fixupPCIDescription(dev_product); let product = Util.fixupPCIDescription(dev_product);
let vendor = Util.fixupPCIDescription(dev_vendor); let vendor = Util.fixupPCIDescription(dev_vendor);
@ -734,6 +748,10 @@ NMDeviceModem.prototype = {
this.mobileDevice = null; this.mobileDevice = null;
this._connectionType = 'ppp'; 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; this._capabilities = device.current_capabilities;
if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) { if (this._capabilities & NetworkManager.DeviceModemCapabilities.GSM_UMTS) {
is_wwan = true; is_wwan = true;
@ -835,10 +853,12 @@ NMDeviceModem.prototype = {
}, },
_createAutomaticConnection: function() { _createAutomaticConnection: function() {
// Mobile wizard is too complex for the shell UI and // Mobile wizard is handled by nm-applet for now...
// is handled by the network panel this._applet_proxy.ConnectTo3gNetworkRemote(this.device.get_path(),
Util.spawn(['gnome-control-center', 'network', Lang.bind(this, function(results, err) {
'connect-3g', this.device.get_path()]); if (err)
log(err);
}));
return null; return null;
} }
}; };
@ -950,6 +970,10 @@ NMDeviceWireless.prototype = {
this._overflowItem = null; this._overflowItem = null;
this._networks = [ ]; 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 // breaking the layers with this, but cannot call
// this.connectionValid until I have a device // this.connectionValid until I have a device
this.device = device; this.device = device;
@ -961,16 +985,6 @@ NMDeviceWireless.prototype = {
for (let i = 0; i < accessPoints.length; i++) { for (let i = 0; i < accessPoints.length; i++) {
// Access points are grouped by network // Access points are grouped by network
let ap = accessPoints[i]; 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 pos = this._findNetwork(ap);
let obj; let obj;
if (pos != -1) { if (pos != -1) {
@ -984,7 +998,7 @@ NMDeviceWireless.prototype = {
item: null, item: null,
accessPoints: [ ap ] accessPoints: [ ap ]
}; };
obj.ssidText = ssidToLabel(obj.ssid); obj.ssidText = NetworkManager.utils_ssid_to_utf8(obj.ssid);
this._networks.push(obj); this._networks.push(obj);
} }
@ -997,14 +1011,8 @@ NMDeviceWireless.prototype = {
} }
} }
} }
if (this.device.active_access_point) { if (this.device.active_access_point) {
let networkPos = this._findNetwork(this.device.active_access_point); this._activeNetwork = this._networks[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 { } else {
this._activeNetwork = null; this._activeNetwork = null;
} }
@ -1090,14 +1098,6 @@ NMDeviceWireless.prototype = {
} }
}, },
_notifySsidCb: function(accessPoint) {
if (accessPoint.get_ssid() != null) {
accessPoint.disconnect(accessPoint._notifySsidId);
accessPoint._notifySsidId = 0;
this._accessPointAdded(this.device, accessPoint);
}
},
_activeApChanged: function() { _activeApChanged: function() {
this._activeNetwork = null; this._activeNetwork = null;
@ -1105,9 +1105,7 @@ NMDeviceWireless.prototype = {
if (activeAp) { if (activeAp) {
let pos = this._findNetwork(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 // we don't refresh the view here, setActiveConnection will
@ -1182,9 +1180,6 @@ NMDeviceWireless.prototype = {
}, },
_findNetwork: function(accessPoint) { _findNetwork: function(accessPoint) {
if (accessPoint.get_ssid() == null)
return -1;
for (let i = 0; i < this._networks.length; i++) { for (let i = 0; i < this._networks.length; i++) {
if (this._networkCompare(this._networks[i], accessPoint)) if (this._networkCompare(this._networks[i], accessPoint))
return i; return i;
@ -1193,13 +1188,6 @@ NMDeviceWireless.prototype = {
}, },
_accessPointAdded: function(device, accessPoint) { _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 pos = this._findNetwork(accessPoint);
let apObj; let apObj;
let needsupdate = false; let needsupdate = false;
@ -1222,7 +1210,7 @@ NMDeviceWireless.prototype = {
item: null, item: null,
accessPoints: [ accessPoint ] accessPoints: [ accessPoint ]
}; };
apObj.ssidText = ssidToLabel(apObj.ssid); apObj.ssidText = NetworkManager.utils_ssid_to_utf8(apObj.ssid);
needsupdate = true; needsupdate = true;
} }
@ -1291,9 +1279,6 @@ NMDeviceWireless.prototype = {
apObj.accessPoints.splice(i, 1); apObj.accessPoints.splice(i, 1);
if (apObj.accessPoints.length == 0) { if (apObj.accessPoints.length == 0) {
if (this._activeNetwork == apObj)
this._activeNetwork = null;
if (apObj.item) if (apObj.item)
apObj.item.destroy(); apObj.item.destroy();
@ -1438,12 +1423,13 @@ NMDeviceWireless.prototype = {
}, },
_createActiveConnectionItem: function() { _createActiveConnectionItem: function() {
let activeAp = this.device.active_access_point;
let icon, title; let icon, title;
if (this._activeConnection._connection) { if (this._activeConnection._connection) {
let connection = this._activeConnection._connection; let connection = this._activeConnection._connection;
if (this._activeNetwork) if (activeAp)
this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined, this._activeConnectionItem = new NMNetworkMenuItem([ activeAp ], undefined,
{ reactive: false }); { reactive: false });
else else
this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(connection._name, this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(connection._name,
'network-wireless-connected', 'network-wireless-connected',
@ -1451,9 +1437,9 @@ NMDeviceWireless.prototype = {
} else { } else {
// We cannot read the connection (due to ACL, or API incompatibility), but we still show signal if we have it // We cannot read the connection (due to ACL, or API incompatibility), but we still show signal if we have it
let menuItem; let menuItem;
if (this._activeNetwork) if (activeAp)
this._activeConnectionItem = new NMNetworkMenuItem(this._activeNetwork.accessPoints, undefined, this._activeConnectionItem = new NMNetworkMenuItem([ activeAp ], undefined,
{ reactive: false }); { reactive: false });
else else
this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(_("Connected (private)"), this._activeConnectionItem = new PopupMenu.PopupImageMenuItem(_("Connected (private)"),
'network-wireless-connected', 'network-wireless-connected',
@ -1483,11 +1469,6 @@ NMDeviceWireless.prototype = {
}, },
_createNetworkItem: function(apObj, position) { _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 > 0) {
if (apObj.connections.length == 1) if (apObj.connections.length == 1)
apObj.item = this._createAPItem(apObj.connections[0], apObj, false); apObj.item = this._createAPItem(apObj.connections[0], apObj, false);
@ -1505,10 +1486,13 @@ NMDeviceWireless.prototype = {
let accessPoints = sortAccessPoints(apObj.accessPoints); let accessPoints = sortAccessPoints(apObj.accessPoints);
if ( (accessPoints[0]._secType == NMAccessPointSecurity.WPA2_ENT) if ( (accessPoints[0]._secType == NMAccessPointSecurity.WPA2_ENT)
|| (accessPoints[0]._secType == NMAccessPointSecurity.WPA_ENT)) { || (accessPoints[0]._secType == NMAccessPointSecurity.WPA_ENT)) {
// 802.1x-enabled APs require further configuration, so they're // 802.1x-enabled APs get handled by nm-applet for now...
// handled in gnome-control-center this._applet_proxy.ConnectTo8021xNetworkRemote(this.device.get_path(),
Util.spawn(['gnome-control-center', 'network', 'connect-8021x-wifi', accessPoints[0].dbus_path,
this.device.get_path(), accessPoints[0].dbus_path]); Lang.bind(this, function(results, err) {
if (err)
log(err);
}));
} else { } else {
let connection = this._createAutomaticConnection(apObj); let connection = this._createAutomaticConnection(apObj);
this._client.add_and_activate_connection(connection, this.device, accessPoints[0].dbus_path, null) this._client.add_and_activate_connection(connection, this.device, accessPoints[0].dbus_path, null)
@ -1566,9 +1550,9 @@ NMApplet.prototype = {
this._statusSection.addAction(_("Enable networking"), Lang.bind(this, function() { this._statusSection.addAction(_("Enable networking"), Lang.bind(this, function() {
this._client.networking_enabled = true; this._client.networking_enabled = true;
})); }));
this._statusSection.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._statusSection.actor.hide(); this._statusSection.actor.hide();
this.menu.addMenuItem(this._statusSection); this.menu.addMenuItem(this._statusSection);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices = { }; this._devices = { };
@ -1579,9 +1563,9 @@ NMApplet.prototype = {
}; };
this._devices.wired.section.addMenuItem(this._devices.wired.item); this._devices.wired.section.addMenuItem(this._devices.wired.item);
this._devices.wired.section.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.wired.section.actor.hide(); this._devices.wired.section.actor.hide();
this.menu.addMenuItem(this._devices.wired.section); this.menu.addMenuItem(this._devices.wired.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.wireless = { this._devices.wireless = {
section: new PopupMenu.PopupMenuSection(), section: new PopupMenu.PopupMenuSection(),
@ -1589,9 +1573,9 @@ NMApplet.prototype = {
item: this._makeToggleItem('wireless', _("Wireless")) item: this._makeToggleItem('wireless', _("Wireless"))
}; };
this._devices.wireless.section.addMenuItem(this._devices.wireless.item); this._devices.wireless.section.addMenuItem(this._devices.wireless.item);
this._devices.wireless.section.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.wireless.section.actor.hide(); this._devices.wireless.section.actor.hide();
this.menu.addMenuItem(this._devices.wireless.section); this.menu.addMenuItem(this._devices.wireless.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.wwan = { this._devices.wwan = {
section: new PopupMenu.PopupMenuSection(), section: new PopupMenu.PopupMenuSection(),
@ -1599,9 +1583,9 @@ NMApplet.prototype = {
item: this._makeToggleItem('wwan', _("Mobile broadband")) item: this._makeToggleItem('wwan', _("Mobile broadband"))
}; };
this._devices.wwan.section.addMenuItem(this._devices.wwan.item); this._devices.wwan.section.addMenuItem(this._devices.wwan.item);
this._devices.wwan.section.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.wwan.section.actor.hide(); this._devices.wwan.section.actor.hide();
this.menu.addMenuItem(this._devices.wwan.section); this.menu.addMenuItem(this._devices.wwan.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._devices.vpn = { this._devices.vpn = {
section: new PopupMenu.PopupMenuSection(), section: new PopupMenu.PopupMenuSection(),
@ -1614,10 +1598,15 @@ NMApplet.prototype = {
this._devices.vpn.item.updateForDevice(this._devices.vpn.device); 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.item);
this._devices.vpn.section.addMenuItem(this._devices.vpn.device.section); 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._devices.vpn.section.actor.hide();
this.menu.addMenuItem(this._devices.vpn.section); this.menu.addMenuItem(this._devices.vpn.section);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop'); 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._activeConnections = [ ]; this._activeConnections = [ ];
this._connections = [ ]; this._connections = [ ];
@ -1671,7 +1660,8 @@ NMApplet.prototype = {
_ensureSource: function() { _ensureSource: function() {
if (!this._source) { if (!this._source) {
this._source = new NMMessageTraySource(); this._source = new NMMessageTraySource();
this._source.connect('destroy', Lang.bind(this, function() { this._source._destroyId = this._source.connect('destroy', Lang.bind(this, function() {
this._source._destroyId = 0;
this._source = null; this._source = null;
})); }));
Main.messageTray.add(this._source); Main.messageTray.add(this._source);
@ -1719,28 +1709,6 @@ 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) { _deviceAdded: function(client, device) {
if (device._delegate) { if (device._delegate) {
// already seen, not adding again // already seen, not adding again
@ -1750,21 +1718,42 @@ NMApplet.prototype = {
if (wrapperClass) { if (wrapperClass) {
let wrapper = new wrapperClass(this._client, device, this._connections); let wrapper = new wrapperClass(this._client, device, this._connections);
wrapper._activationFailedId = wrapper.connect('activation-failed', Lang.bind(this, function(device, reason) { // 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;
// XXX: nm-applet has no special text depending on reason // XXX: nm-applet has no special text depending on reason
// but I'm not sure of this generic message // but I'm not sure of this generic message
this._notifyForDevice(device, 'network-error', let notification = new MessageTray.Notification(this._source,
_("Connection failed"), _("Connection failed"),
_("Activation of network connection failed"), _("Activation of network connection failed"),
MessageTray.Urgency.HIGH); { icon: icon });
})); this._source.notify(notification);
})); */
wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) { wrapper._deviceStateChangedId = wrapper.connect('state-changed', Lang.bind(this, function(dev) {
this._syncSectionTitle(dev.category); this._syncSectionTitle(dev.category);
})); }));
wrapper._destroyId = wrapper.connect('destroy', function(wrapper) { wrapper._destroyId = wrapper.connect('destroy', function(wrapper) {
wrapper.disconnect(wrapper._activationFailedId); //wrapper.disconnect(wrapper._networkLostId);
//wrapper.disconnect(wrapper._activationFailedId);
wrapper.disconnect(wrapper._deviceStateChangedId); wrapper.disconnect(wrapper._deviceStateChangedId);
wrapper.disconnect(wrapper._destroyId);
}); });
let section = this._devices[wrapper.category].section; let section = this._devices[wrapper.category].section;
let devices = this._devices[wrapper.category].devices; let devices = this._devices[wrapper.category].devices;
@ -1809,8 +1798,11 @@ NMApplet.prototype = {
active._primaryDevice.setActiveConnection(null); active._primaryDevice.setActiveConnection(null);
active._primaryDevice = null; active._primaryDevice = null;
} }
if (active._inited) { if (active._notifyStateId) {
active.disconnect(active._notifyStateId); active.disconnect(active._notifyStateId);
active._notifyStateId = 0;
}
if (active._inited) {
active.disconnect(active._notifyDefaultId); active.disconnect(active._notifyDefaultId);
active.disconnect(active._notifyDefault6Id); active.disconnect(active._notifyDefault6Id);
active._inited = false; active._inited = false;
@ -1828,7 +1820,14 @@ NMApplet.prototype = {
if (!a._inited) { if (!a._inited) {
a._notifyDefaultId = a.connect('notify::default', Lang.bind(this, this._updateIcon)); a._notifyDefaultId = a.connect('notify::default', Lang.bind(this, this._updateIcon));
a._notifyDefault6Id = a.connect('notify::default6', Lang.bind(this, this._updateIcon)); a._notifyDefault6Id = a.connect('notify::default6', Lang.bind(this, this._updateIcon));
a._notifyStateId = a.connect('notify::state', Lang.bind(this, this._notifyActivated)); 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._inited = true; a._inited = true;
} }
@ -1872,26 +1871,67 @@ NMApplet.prototype = {
if (a._primaryDevice) if (a._primaryDevice)
a._primaryDevice.setActiveConnection(a); 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; this._mainConnection = activating || default_ip4 || default_ip6 || this._activeConnections[0] || null;
}, },
_notifyActivated: function(activeConnection) { _notifyActiveConnection: function(active) {
if (activeConnection.state == NetworkManager.ActiveConnectionState.ACTIVATED // FIXME: duplicate notifications when nm-applet is running
&& activeConnection._primaryDevice && activeConnection._primaryDevice._notification) { // This code will come back when nm-applet is killed
activeConnection._primaryDevice._notification.destroy(); this._syncNMState();
activeConnection._primaryDevice._notification = null; 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;
}
} }
this._updateIcon(); this._syncNMState();
}, },
_readConnections: function() { _readConnections: function() {

View File

@ -75,11 +75,16 @@ Indicator.prototype = {
this._batteryItem.addActor(this._primaryPercentage, { align: St.Align.END }); this._batteryItem.addActor(this._primaryPercentage, { align: St.Align.END });
this.menu.addMenuItem(this._batteryItem); this.menu.addMenuItem(this._batteryItem);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this._deviceSep = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(this._deviceSep);
this._otherDevicePosition = 2; this._otherDevicePosition = 2;
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
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._proxy.connect('Changed', Lang.bind(this, this._devicesChanged)); this._proxy.connect('Changed', Lang.bind(this, this._devicesChanged));
this._devicesChanged(); this._devicesChanged();
@ -91,6 +96,7 @@ Indicator.prototype = {
this._hasPrimary = false; this._hasPrimary = false;
this._primaryDeviceId = null; this._primaryDeviceId = null;
this._batteryItem.actor.hide(); this._batteryItem.actor.hide();
this._deviceSep.actor.hide();
return; return;
} }
let [device_id, device_type, icon, percentage, state, seconds] = device; let [device_id, device_type, icon, percentage, state, seconds] = device;
@ -118,11 +124,14 @@ Indicator.prototype = {
timestring = ngettext("%d minute remaining", "%d minutes remaining", minutes).format(minutes); timestring = ngettext("%d minute remaining", "%d minutes remaining", minutes).format(minutes);
this._batteryItem.label.text = timestring; this._batteryItem.label.text = timestring;
} }
this._primaryPercentage.text = C_("percent of battery remaining", "%d%%").format(Math.round(percentage)); this._primaryPercentage.text = Math.round(percentage) + '%';
this._batteryItem.actor.show(); this._batteryItem.actor.show();
if (this._deviceItems.length > 0)
this._deviceSep.actor.show();
} else { } else {
this._hasPrimary = false; this._hasPrimary = false;
this._batteryItem.actor.hide(); this._batteryItem.actor.hide();
this._deviceSep.actor.hide();
} }
this._primaryDeviceId = device_id; this._primaryDeviceId = device_id;
@ -135,6 +144,7 @@ Indicator.prototype = {
this._deviceItems = []; this._deviceItems = [];
if (error) { if (error) {
this._deviceSep.actor.hide();
return; return;
} }
@ -149,6 +159,11 @@ Indicator.prototype = {
this.menu.addMenuItem(item, this._otherDevicePosition + position); this.menu.addMenuItem(item, this._otherDevicePosition + position);
position++; position++;
} }
if (this._hasPrimary && position > 0)
this._deviceSep.actor.show();
else
this._deviceSep.actor.hide();
})); }));
}, },
@ -191,7 +206,7 @@ DeviceItem.prototype = {
this._box.add_actor(this._label); this._box.add_actor(this._label);
this.addActor(this._box); this.addActor(this._box);
let percentLabel = new St.Label({ text: C_("percent of battery remaining", "%d%%").format(Math.round(percentage)) }); let percentLabel = new St.Label({ text: '%d%%'.format(Math.round(percentage)) });
this.addActor(percentLabel, { align: St.Align.END }); this.addActor(percentLabel, { align: St.Align.END });
}, },

View File

@ -29,12 +29,13 @@ Indicator.prototype = {
PanelMenu.SystemStatusButton.prototype._init.call(this, 'audio-volume-muted', null); PanelMenu.SystemStatusButton.prototype._init.call(this, 'audio-volume-muted', null);
this._control = new Gvc.MixerControl({ name: 'GNOME Shell Volume Control' }); this._control = new Gvc.MixerControl({ name: 'GNOME Shell Volume Control' });
this._control.connect('state-changed', Lang.bind(this, this._onControlStateChanged)); this._control.connect('ready', Lang.bind(this, this._onControlReady));
this._control.connect('default-sink-changed', Lang.bind(this, this._readOutput)); 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('default-source-changed', Lang.bind(this, this._readInput));
this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput)); this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput)); this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
this._volumeMax = this._control.get_vol_max_norm(); this._volumeMax = this._control.get_vol_max_norm();
this._volumeMaxAmplified = this._control.get_vol_max_amplified();
this._output = null; this._output = null;
this._outputVolumeId = 0; this._outputVolumeId = 0;
@ -46,7 +47,8 @@ Indicator.prototype = {
this.menu.addMenuItem(this._outputTitle); this.menu.addMenuItem(this._outputTitle);
this.menu.addMenuItem(this._outputSlider); this.menu.addMenuItem(this._outputSlider);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this._separator = new PopupMenu.PopupSeparatorMenuItem();
this.menu.addMenuItem(this._separator);
this._input = null; this._input = null;
this._inputVolumeId = 0; this._inputVolumeId = 0;
@ -59,19 +61,31 @@ Indicator.prototype = {
this.menu.addMenuItem(this._inputSlider); this.menu.addMenuItem(this._inputSlider);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop'); 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.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent)); this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
this._control.open(); this._control.open();
}, },
_getMaxVolume: function(property) {
if (this[property].get_can_decibel())
return this._volumeMaxAmplified;
else
return this._volumeMax;
},
_onScrollEvent: function(actor, event) { _onScrollEvent: function(actor, event) {
let direction = event.get_scroll_direction(); let direction = event.get_scroll_direction();
let currentVolume = this._output.volume; let currentVolume = this._output.volume;
let maxVolume = this._getMaxVolume('_output');
if (direction == Clutter.ScrollDirection.DOWN) { if (direction == Clutter.ScrollDirection.DOWN) {
let prev_muted = this._output.is_muted; let prev_muted = this._output.is_muted;
this._output.volume = Math.max(0, currentVolume - this._volumeMax * VOLUME_ADJUSTMENT_STEP); this._output.volume = Math.max(0, currentVolume - maxVolume * VOLUME_ADJUSTMENT_STEP);
if (this._output.volume < 1) { if (this._output.volume < 1) {
this._output.volume = 0; this._output.volume = 0;
if (!prev_muted) if (!prev_muted)
@ -80,7 +94,7 @@ Indicator.prototype = {
this._output.push_volume(); this._output.push_volume();
} }
else if (direction == Clutter.ScrollDirection.UP) { else if (direction == Clutter.ScrollDirection.UP) {
this._output.volume = Math.min(this._volumeMax, currentVolume + this._volumeMax * VOLUME_ADJUSTMENT_STEP); this._output.volume = Math.min(maxVolume, currentVolume + maxVolume * VOLUME_ADJUSTMENT_STEP);
this._output.change_is_muted(false); this._output.change_is_muted(false);
this._output.push_volume(); this._output.push_volume();
} }
@ -88,14 +102,9 @@ Indicator.prototype = {
this._notifyVolumeChange(); this._notifyVolumeChange();
}, },
_onControlStateChanged: function() { _onControlReady: function() {
if (this._control.get_state() == Gvc.MixerControlState.READY) { this._readOutput();
this._readOutput(); this._readInput();
this._readInput();
this.actor.show();
} else {
this.actor.hide();
}
}, },
_readOutput: function() { _readOutput: function() {
@ -131,6 +140,7 @@ Indicator.prototype = {
this._mutedChanged (null, null, '_input'); this._mutedChanged (null, null, '_input');
this._volumeChanged (null, null, '_input'); this._volumeChanged (null, null, '_input');
} else { } else {
this._separator.actor.hide();
this._inputTitle.actor.hide(); this._inputTitle.actor.hide();
this._inputSlider.actor.hide(); this._inputSlider.actor.hide();
} }
@ -153,19 +163,22 @@ Indicator.prototype = {
} }
} }
if (showInput) { if (showInput) {
this._separator.actor.show();
this._inputTitle.actor.show(); this._inputTitle.actor.show();
this._inputSlider.actor.show(); this._inputSlider.actor.show();
} else { } else {
this._separator.actor.hide();
this._inputTitle.actor.hide(); this._inputTitle.actor.hide();
this._inputSlider.actor.hide(); this._inputSlider.actor.hide();
} }
}, },
_volumeToIcon: function(volume) { _volumeToIcon: function(volume) {
let maxVolume = this._getMaxVolume('_output');
if (volume <= 0) { if (volume <= 0) {
return 'audio-volume-muted'; return 'audio-volume-muted';
} else { } else {
let n = Math.floor(3 * volume / this._volumeMax) + 1; let n = Math.floor(3 * volume / maxVolume) + 1;
if (n < 2) if (n < 2)
return 'audio-volume-low'; return 'audio-volume-low';
if (n >= 3) if (n >= 3)
@ -179,7 +192,7 @@ Indicator.prototype = {
log ('Volume slider changed for %s, but %s does not exist'.format(property, property)); log ('Volume slider changed for %s, but %s does not exist'.format(property, property));
return; return;
} }
let volume = value * this._volumeMax; let volume = value * this._getMaxVolume(property);
let prev_muted = this[property].is_muted; let prev_muted = this[property].is_muted;
if (volume < 1) { if (volume < 1) {
this[property].volume = 0; this[property].volume = 0;
@ -201,7 +214,8 @@ Indicator.prototype = {
_mutedChanged: function(object, param_spec, property) { _mutedChanged: function(object, param_spec, property) {
let muted = this[property].is_muted; let muted = this[property].is_muted;
let slider = this[property+'Slider']; let slider = this[property+'Slider'];
slider.setValue(muted ? 0 : (this[property].volume / this._volumeMax)); let maxVolume = this._getMaxVolume(property);
slider.setValue(muted ? 0 : (this[property].volume / maxVolume));
if (property == '_output') { if (property == '_output') {
if (muted) if (muted)
this.setIcon('audio-volume-muted'); this.setIcon('audio-volume-muted');
@ -211,7 +225,8 @@ Indicator.prototype = {
}, },
_volumeChanged: function(object, param_spec, property) { _volumeChanged: function(object, param_spec, property) {
this[property+'Slider'].setValue(this[property].volume / this._volumeMax); let maxVolume = this._getMaxVolume(property);
this[property+'Slider'].setValue(this[property].volume / maxVolume);
if (property == '_output' && !this._output.is_muted) if (property == '_output' && !this._output.is_muted)
this.setIcon(this._volumeToIcon(this._output.volume)); this.setIcon(this._volumeToIcon(this._output.volume));
} }

View File

@ -4,6 +4,7 @@ const Lang = imports.lang;
const Shell = imports.gi.Shell; const Shell = imports.gi.Shell;
const Signals = imports.signals; const Signals = imports.signals;
const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray; const MessageTray = imports.ui.messageTray;
const NotificationDaemon = imports.ui.notificationDaemon; const NotificationDaemon = imports.ui.notificationDaemon;
const Util = imports.misc.util; const Util = imports.misc.util;
@ -38,10 +39,10 @@ StatusIconDispatcher.prototype = {
// status icons // status icons
// http://bugzilla.gnome.org/show_bug.cgi=id=621382 // http://bugzilla.gnome.org/show_bug.cgi=id=621382
Util.killall('indicator-application-service'); Util.killall('indicator-application-service');
},
start: function(themeWidget) { Main.connect('initialized', Lang.bind(this, function() {
this._traymanager.manage_stage(global.stage, themeWidget); this._traymanager.manage_stage(global.stage, Main.messageTray.actor);
}));
}, },
_onTrayIconAdded: function(o, icon) { _onTrayIconAdded: function(o, icon) {

345
js/ui/statusMenu.js Normal file
View File

@ -0,0 +1,345 @@
/* -*- 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,7 +1,6 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const DBus = imports.dbus; const DBus = imports.dbus;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib; const GLib = imports.gi.GLib;
const Lang = imports.lang; const Lang = imports.lang;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
@ -12,7 +11,6 @@ const Tpl = imports.gi.TelepathyLogger;
const Tp = imports.gi.TelepathyGLib; const Tp = imports.gi.TelepathyGLib;
const History = imports.misc.history; const History = imports.misc.history;
const Params = imports.misc.params;
const Main = imports.ui.main; const Main = imports.ui.main;
const MessageTray = imports.ui.messageTray; const MessageTray = imports.ui.messageTray;
@ -45,16 +43,11 @@ let contactFeatures = [Tp.ContactFeature.ALIAS,
function makeMessageFromTpMessage(tpMessage, direction) { function makeMessageFromTpMessage(tpMessage, direction) {
let [text, flags] = tpMessage.to_text(); let [text, flags] = tpMessage.to_text();
let timestamp = tpMessage.get_sent_timestamp();
if (timestamp == 0)
timestamp = tpMessage.get_received_timestamp();
return { return {
messageType: tpMessage.get_message_type(), messageType: tpMessage.get_message_type(),
text: text, text: text,
sender: tpMessage.sender.alias, sender: tpMessage.sender.alias,
timestamp: timestamp, timestamp: tpMessage.get_received_timestamp(),
direction: direction direction: direction
}; };
} }
@ -83,15 +76,12 @@ Client.prototype = {
this._chatSources = {}; this._chatSources = {};
this._chatState = Tp.ChannelChatState.ACTIVE; this._chatState = Tp.ChannelChatState.ACTIVE;
// account path -> AccountNotification
this._accountNotifications = {};
// Set up a SimpleObserver, which will call _observeChannels whenever a // Set up a SimpleObserver, which will call _observeChannels whenever a
// channel matching its filters is detected. // channel matching its filters is detected.
// The second argument, recover, means _observeChannels will be run // The second argument, recover, means _observeChannels will be run
// for any existing channel as well. // for any existing channel as well.
this._accountManager = Tp.AccountManager.dup(); let dbus = Tp.DBusDaemon.dup();
this._tpClient = new Shell.TpClient({ 'account-manager': this._accountManager, this._tpClient = new Shell.TpClient({ 'dbus_daemon': dbus,
'name': 'GnomeShell', 'name': 'GnomeShell',
'uniquify-name': true }) 'uniquify-name': true })
this._tpClient.set_observe_channels_func( this._tpClient.set_observe_channels_func(
@ -101,11 +91,6 @@ Client.prototype = {
this._tpClient.set_handle_channels_func( this._tpClient.set_handle_channels_func(
Lang.bind(this, this._handleChannels)); 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 // Allow other clients (such as Empathy) to pre-empt our channels if
// needed // needed
this._tpClient.set_delegated_channels_callback( this._tpClient.set_delegated_channels_callback(
@ -116,22 +101,6 @@ Client.prototype = {
} catch (e) { } catch (e) {
throw new Error('Couldn\'t register Telepathy client. Error: \n' + 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, _observeChannels: function(observer, account, conn, channels,
@ -256,8 +225,7 @@ Client.prototype = {
// FIXME: We don't have a 'chat room' icon (bgo #653737) use // FIXME: We don't have a 'chat room' icon (bgo #653737) use
// system-users for now as Empathy does. // system-users for now as Empathy does.
let source = new ApproverSource(dispatchOp, _("Invitation"), let source = new ApproverSource(dispatchOp, _("Invitation"), 'system-users');
Shell.util_icon_from_string('system-users'));
Main.messageTray.add(source); Main.messageTray.add(source);
let notif = new RoomInviteNotification(source, dispatchOp, channel, contacts[0]); let notif = new RoomInviteNotification(source, dispatchOp, channel, contacts[0]);
@ -275,8 +243,6 @@ Client.prototype = {
else if (chanType == Tp.IFACE_CHANNEL_TYPE_STREAMED_MEDIA || else if (chanType == Tp.IFACE_CHANNEL_TYPE_STREAMED_MEDIA ||
chanType == 'org.freedesktop.Telepathy.Channel.Type.Call.DRAFT') chanType == 'org.freedesktop.Telepathy.Channel.Type.Call.DRAFT')
this._approveCall(account, conn, channel, dispatchOp, context); 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) { _approveTextChannel: function(account, conn, channel, dispatchOp, context) {
@ -305,8 +271,6 @@ Client.prototype = {
Shell.get_tp_contacts(conn, [targetHandle], Shell.get_tp_contacts(conn, [targetHandle],
contactFeatures, contactFeatures,
Lang.bind(this, this._createAudioVideoSource, channel, context, dispatchOp)); Lang.bind(this, this._createAudioVideoSource, channel, context, dispatchOp));
context.delay();
}, },
_createAudioVideoSource: function(connection, contacts, failed, channel, context, dispatchOp) { _createAudioVideoSource: function(connection, contacts, failed, channel, context, dispatchOp) {
@ -324,9 +288,7 @@ Client.prototype = {
isVideo = true; isVideo = true;
// We got the TpContact // We got the TpContact
let source = new ApproverSource(dispatchOp, _("Call"), isVideo ? let source = new ApproverSource(dispatchOp, _("Call"), isVideo ? 'camera-web' : 'audio-input-microphone');
Shell.util_icon_from_string('camera-web') :
Shell.util_icon_from_string('audio-input-microphone'));
Main.messageTray.add(source); Main.messageTray.add(source);
let notif = new AudioVideoNotification(source, dispatchOp, channel, contacts[0], isVideo); let notif = new AudioVideoNotification(source, dispatchOp, channel, contacts[0], isVideo);
@ -334,151 +296,9 @@ Client.prototype = {
context.accept(); 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));
context.delay();
},
_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) { _delegatedChannelsCb: function(client, channels) {
// Nothing to do as we don't make a distinction between observed and // Nothing to do as we don't make a distinction between observed and
// handled channels. // 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;
} }
}; };
@ -506,7 +326,6 @@ ChatSource.prototype = {
this._notification = new ChatNotification(this); this._notification = new ChatNotification(this);
this._notification.setUrgency(MessageTray.Urgency.HIGH); this._notification.setUrgency(MessageTray.Urgency.HIGH);
this._notifyTimeoutId = 0;
// We ack messages when the message box is collapsed if user has // We ack messages when the message box is collapsed if user has
// interacted with it before and so read the messages: // interacted with it before and so read the messages:
@ -547,6 +366,13 @@ ChatSource.prototype = {
createNotificationIcon: function() { createNotificationIcon: function() {
this._iconBox = new St.Bin({ style_class: 'avatar-box' }); this._iconBox = new St.Bin({ style_class: 'avatar-box' });
this._iconBox._size = this.ICON_SIZE; this._iconBox._size = this.ICON_SIZE;
this._updateAvatarIcon();
return this._iconBox;
},
_updateAvatarIcon: function() {
let textureCache = St.TextureCache.get_default(); let textureCache = St.TextureCache.get_default();
let file = this._contact.get_avatar_file(); let file = this._contact.get_avatar_file();
@ -558,13 +384,6 @@ ChatSource.prototype = {
icon_type: St.IconType.FULLCOLOR, icon_type: St.IconType.FULLCOLOR,
icon_size: this._iconBox._size }); icon_size: this._iconBox._size });
} }
return this._iconBox;
},
_updateAvatarIcon: function() {
this._setSummaryIcon(this.createNotificationIcon());
this._notification.update(this._notification.title, null, { customContent: true, icon: this.createNotificationIcon() });
}, },
open: function(notification) { open: function(notification) {
@ -668,22 +487,7 @@ ChatSource.prototype = {
message = makeMessageFromTpMessage(message, NotificationDirection.RECEIVED); message = makeMessageFromTpMessage(message, NotificationDirection.RECEIVED);
this._notification.appendMessage(message); 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 // This is called for both messages we send from
@ -772,10 +576,13 @@ ChatSource.prototype = {
}, },
_ackMessages: function() { _ackMessages: function() {
if (this._pendingMessages.length == 0)
return;
// Don't clear our messages here, tp-glib will send a // Don't clear our messages here, tp-glib will send a
// 'pending-message-removed' for each one. // 'pending-message-removed' for each one.
this._channel.ack_all_pending_messages_async(Lang.bind(this, function(src, result) { this._channel.ack_messages_async(this._pendingMessages, Lang.bind(this, function(src, result) {
this._channel.ack_all_pending_messages_finish(result);})); this._channel.ack_messages_finish(result);}));
}, },
_summaryItemClicked: function(source, button) { _summaryItemClicked: function(source, button) {
@ -816,8 +623,6 @@ ChatNotification.prototype = {
this._oldMaxScrollAdjustment = 0; this._oldMaxScrollAdjustment = 0;
this._createScrollArea(); this._createScrollArea();
this._lastGroup = null;
this._lastGroupActor = null;
this._scrollArea.vscroll.adjustment.connect('changed', Lang.bind(this, function(adjustment) { this._scrollArea.vscroll.adjustment.connect('changed', Lang.bind(this, function(adjustment) {
let currentValue = adjustment.value + adjustment.page_size; let currentValue = adjustment.value + adjustment.page_size;
@ -845,10 +650,12 @@ ChatNotification.prototype = {
* @noTimestamp: Whether to add a timestamp. If %true, no timestamp * @noTimestamp: Whether to add a timestamp. If %true, no timestamp
* will be added, regardless of the difference since the * will be added, regardless of the difference since the
* last timestamp * last timestamp
* @styles: A list of CSS class names.
*/ */
appendMessage: function(message, noTimestamp) { appendMessage: function(message, noTimestamp, styles) {
let messageBody = GLib.markup_escape_text(message.text, -1); let messageBody = GLib.markup_escape_text(message.text, -1);
let styles = [message.direction]; styles = styles || [];
styles.push(message.direction);
if (message.messageType == Tp.ChannelTextMessageType.ACTION) { if (message.messageType == Tp.ChannelTextMessageType.ACTION) {
let senderAlias = GLib.markup_escape_text(message.sender, -1); let senderAlias = GLib.markup_escape_text(message.sender, -1);
@ -861,14 +668,7 @@ ChatNotification.prototype = {
bannerMarkup: true }); bannerMarkup: true });
} }
let group = (message.direction == NotificationDirection.RECEIVED ? this._append(messageBody, styles, message.timestamp, noTimestamp);
'received' : 'sent');
this._append({ body: messageBody,
group: group,
styles: styles,
timestamp: message.timestamp,
noTimestamp: noTimestamp });
}, },
_filterMessages: function() { _filterMessages: function() {
@ -894,65 +694,24 @@ ChatNotification.prototype = {
for (let i = 0; i < expired.length; i++) for (let i = 0; i < expired.length; i++)
expired[i].actor.destroy(); 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); let currentTime = (Date.now() / 1000);
props = Params.parse(props, { body: null, if (!timestamp)
group: null, timestamp = currentTime;
styles: [],
timestamp: currentTime,
noTimestamp: false,
childProps: null });
// Reset the old message timeout // Reset the old message timeout
if (this._timestampTimeoutId) if (this._timestampTimeoutId)
Mainloop.source_remove(this._timestampTimeoutId); Mainloop.source_remove(this._timestampTimeoutId);
let highlighter = new MessageTray.URLHighlighter(props.body, let body = this.addBody(text, true);
true, // line wrap?
true); // allow markup?
let body = highlighter.actor;
let styles = props.styles;
for (let i = 0; i < styles.length; i ++) for (let i = 0; i < styles.length; i ++)
body.add_style_class_name(styles[i]); body.add_style_class_name(styles[i]);
let group = props.group; this._history.unshift({ actor: body, time: timestamp, realMessage: true });
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);
}
this._lastGroupActor.add(body, props.childProps); if (!noTimestamp) {
let timestamp = props.timestamp;
this._history.unshift({ actor: body, time: timestamp,
realMessage: group != 'meta' });
if (!props.noTimestamp) {
if (timestamp < currentTime - SCROLLBACK_IMMEDIATE_TIME) if (timestamp < currentTime - SCROLLBACK_IMMEDIATE_TIME)
this.appendTimestamp(); this.appendTimestamp();
else else
@ -979,18 +738,18 @@ ChatNotification.prototype = {
If applicable, replace %X with a strftime format valid for your If applicable, replace %X with a strftime format valid for your
locale, without seconds. */ locale, without seconds. */
// xgettext:no-c-format // xgettext:no-c-format
format = _("Sent at <b>%X</b> on <b>%A</b>"); 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.
} else if (date.getYear() == now.getYear()) { } else if (date.getYear() == now.getYear()) {
/* Translators: this is a time format in the style of "Wednesday, May 25", /* Translators: Shown on calendar heading when selected day occurs on current year */
shown when you get a chat message in the same year. */ format = C_("calendar heading", "%A, %B %d");
// xgettext:no-c-format
format = _("Sent on <b>%A</b>, <b>%B %d</b>");
} else { } else {
/* Translators: this is a time format in the style of "Wednesday, May 25, 2012", /* Translators: Shown on calendar heading when selected day occurs on different year */
shown when you get a chat message in a different year. */ format = C_("calendar heading", "%A, %B %d, %Y");
// xgettext:no-c-format
format = _("Sent on <b>%A</b>, <b>%B %d</b>, %Y");
} }
return date.toLocaleFormat(format); return date.toLocaleFormat(format);
@ -1000,13 +759,11 @@ ChatNotification.prototype = {
let lastMessageTime = this._history[0].time; let lastMessageTime = this._history[0].time;
let lastMessageDate = new Date(lastMessageTime * 1000); let lastMessageDate = new Date(lastMessageTime * 1000);
let timeLabel = this._append({ body: this._formatTimestamp(lastMessageDate), let timeLabel = this.addBody(this._formatTimestamp(lastMessageDate), false, { expand: true, x_fill: false, x_align: St.Align.END });
group: 'meta', timeLabel.add_style_class_name('chat-meta-message');
styles: ['chat-meta-message'], this._history.unshift({ actor: timeLabel, time: lastMessageTime, realMessage: false });
childProps: { expand: true, x_fill: false,
x_align: St.Align.END }, this._timestampTimeoutId = 0;
noTimestamp: true,
timestamp: lastMessageTime });
this._filterMessages(); this._filterMessages();
@ -1018,10 +775,9 @@ ChatNotification.prototype = {
this.update(text, null, { customContent: true, titleMarkup: true }); this.update(text, null, { customContent: true, titleMarkup: true });
else else
this.update(this.source.title, null, { customContent: true }); this.update(this.source.title, null, { customContent: true });
let label = this.addBody(text, true);
let label = this._append({ body: text, label.add_style_class_name('chat-meta-message');
group: 'meta', this._history.unshift({ actor: label, time: (Date.now() / 1000), realMessage: false});
styles: ['chat-meta-message'] });
this._filterMessages(); this._filterMessages();
}, },
@ -1033,11 +789,9 @@ ChatNotification.prototype = {
/* Translators: this is the other person changing their old IM name to their new /* Translators: this is the other person changing their old IM name to their new
IM name. */ IM name. */
let message = '<i>' + _("%s is now known as %s").format(oldAlias, newAlias) + '</i>'; let message = '<i>' + _("%s is now known as %s").format(oldAlias, newAlias) + '</i>';
let label = this.addBody(message, true);
let label = this._append({ body: message, label.add_style_class_name('chat-meta-message');
group: 'meta', this._history.unshift({ actor: label, time: (Date.now() / 1000), realMessage: false });
styles: ['chat-meta-message'] });
this.update(newAlias, null, { customContent: true }); this.update(newAlias, null, { customContent: true });
this._filterMessages(); this._filterMessages();
@ -1090,17 +844,17 @@ ChatNotification.prototype = {
} }
}; };
function ApproverSource(dispatchOp, text, gicon) { function ApproverSource(dispatchOp, text, icon) {
this._init(dispatchOp, text, gicon); this._init(dispatchOp, text, icon);
} }
ApproverSource.prototype = { ApproverSource.prototype = {
__proto__: MessageTray.Source.prototype, __proto__: MessageTray.Source.prototype,
_init: function(dispatchOp, text, gicon) { _init: function(dispatchOp, text, icon) {
MessageTray.Source.prototype._init.call(this, text); MessageTray.Source.prototype._init.call(this, text);
this._gicon = gicon; this._icon = icon;
this._setSummaryIcon(this.createNotificationIcon()); this._setSummaryIcon(this.createNotificationIcon());
this._dispatchOp = dispatchOp; this._dispatchOp = dispatchOp;
@ -1123,7 +877,7 @@ ApproverSource.prototype = {
}, },
createNotificationIcon: function() { createNotificationIcon: function() {
return new St.Icon({ gicon: this._gicon, return new St.Icon({ icon_name: this._icon,
icon_type: St.IconType.FULLCOLOR, icon_type: St.IconType.FULLCOLOR,
icon_size: this.ICON_SIZE }); icon_size: this.ICON_SIZE });
} }
@ -1217,326 +971,3 @@ 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);
}
};

View File

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

View File

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

View File

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

View File

@ -94,44 +94,22 @@ function WindowClone(realWindow) {
WindowClone.prototype = { WindowClone.prototype = {
_init : function(realWindow) { _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.realWindow = realWindow;
this.metaWindow = realWindow.meta_window; this.metaWindow = realWindow.meta_window;
this.metaWindow._delegate = this; this.metaWindow._delegate = this;
this.origX = realWindow.x;
let [borderX, borderY] = this._getInvisibleBorderPadding(); this.origY = realWindow.y;
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._stackAbove = null;
this._sizeChangedId = this.realWindow.connect('size-changed', this._sizeChangedId = this.realWindow.connect('size-changed', Lang.bind(this, function() {
Lang.bind(this, this._onRealWindowSizeChanged)); this.emit('size-changed');
}));
this._realWindowDestroyId = this.realWindow.connect('destroy', this._realWindowDestroyId = this.realWindow.connect('destroy',
Lang.bind(this, this._disconnectRealWindowSignals)); Lang.bind(this, this._disconnectRealWindowSignals));
@ -183,7 +161,7 @@ WindowClone.prototype = {
// will look funny. // will look funny.
if (!this._selected && if (!this._selected &&
this.metaWindow != global.display.focus_window) this.metaWindow != global.screen.get_display().focus_window)
this._zoomEnd(); this._zoomEnd();
} }
}, },
@ -198,32 +176,6 @@ WindowClone.prototype = {
this._realWindowDestroyId = 0; 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() { _onDestroy: function() {
this._disconnectRealWindowSignals(); this._disconnectRealWindowSignals();
@ -1488,7 +1440,7 @@ Workspace.prototype = {
time); time);
return true; return true;
} else if (source.shellWorkspaceLaunch) { } else if (source.shellWorkspaceLaunch) {
source.shellWorkspaceLaunch({ workspace: this.metaWorkspace ? this.metaWorkspace.index() : -1, source.shellWorkspaceLaunch({ workspace: this.metaWorkspace,
timestamp: time }); timestamp: time });
return true; return true;
} }

View File

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

View File

@ -567,6 +567,8 @@ WorkspacesDisplay.prototype = {
this._updateAlwaysZoom(); this._updateAlwaysZoom();
Main.layoutManager.connect('monitors-changed', Lang.bind(this, 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(){ Main.xdndHandler.connect('drag-begin', Lang.bind(this, function(){
this._alwaysZoomOut = true; this._alwaysZoomOut = true;
@ -579,7 +581,6 @@ WorkspacesDisplay.prototype = {
this._switchWorkspaceNotifyId = 0; this._switchWorkspaceNotifyId = 0;
this._nWorkspacesChangedId = 0;
this._itemDragBeginId = 0; this._itemDragBeginId = 0;
this._itemDragCancelledId = 0; this._itemDragCancelledId = 0;
this._itemDragEndId = 0; this._itemDragEndId = 0;
@ -588,7 +589,7 @@ WorkspacesDisplay.prototype = {
this._windowDragEndId = 0; this._windowDragEndId = 0;
}, },
show: function() { show: function() {
this._zoomOut = this._alwaysZoomOut; this._zoomOut = this._alwaysZoomOut;
this._zoomFraction = this._alwaysZoomOut ? 1 : 0; this._zoomFraction = this._alwaysZoomOut ? 1 : 0;
this._updateZoom(); this._updateZoom();
@ -611,9 +612,6 @@ WorkspacesDisplay.prototype = {
global.screen.connect('restacked', global.screen.connect('restacked',
Lang.bind(this, this._onRestacked)); 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) if (this._itemDragBeginId == 0)
this._itemDragBeginId = Main.overview.connect('item-drag-begin', this._itemDragBeginId = Main.overview.connect('item-drag-begin',
Lang.bind(this, this._dragBegin)); Lang.bind(this, this._dragBegin));

View File

@ -1,7 +1,6 @@
af af
an an
ar ar
as
be be
bg bg
bn bn
@ -34,11 +33,9 @@ kn
lt lt
lv lv
mr mr
ms
nb nb
nl nl
nn nn
or
pa pa
pl pl
pt pt

View File

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

1024
po/af.po

File diff suppressed because it is too large Load Diff

1616
po/as.po

File diff suppressed because it is too large Load Diff

1015
po/be.po

File diff suppressed because it is too large Load Diff

1081
po/bg.po

File diff suppressed because it is too large Load Diff

1059
po/ca.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1119
po/cs.po

File diff suppressed because it is too large Load Diff

1137
po/da.po

File diff suppressed because it is too large Load Diff

1014
po/de.po

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1265
po/eo.po

File diff suppressed because it is too large Load Diff

1097
po/es.po

File diff suppressed because it is too large Load Diff

1156
po/eu.po

File diff suppressed because it is too large Load Diff

1238
po/fa.po

File diff suppressed because it is too large Load Diff

1292
po/fi.po

File diff suppressed because it is too large Load Diff

1135
po/fr.po

File diff suppressed because it is too large Load Diff

1029
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

984
po/he.po

File diff suppressed because it is too large Load Diff

1170
po/hi.po

File diff suppressed because it is too large Load Diff

1087
po/hu.po

File diff suppressed because it is too large Load Diff

1198
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

1175
po/ja.po

File diff suppressed because it is too large Load Diff

1095
po/ko.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