~gnome-shell-extensions/gnome-shell-extensions/appindicator-support-head

1 by Jonas Kuemmerlin
initial commit
1
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
2
// Copyright (C) 2011 Giovanni Campagna
3
// Copyright (C) 2013 Jonas Kuemmerlin <rgcjonas@gmail.com>
4
//
5
// This program is free software; you can redistribute it and/or
6
// modify it under the terms of the GNU General Public License
7
// as published by the Free Software Foundation; either version 2
8
// of the License, or (at your option) any later version.
47 by Jonas Kümmerlin
Internal reorganization, mostly in the settings dialog.
9
//
1 by Jonas Kuemmerlin
initial commit
10
// This program is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
// GNU General Public License for more details.
14
//
15
// You should have received a copy of the GNU General Public License
16
// along with this program; if not, write to the Free Software
17
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18
19
const Gio = imports.gi.Gio;
20
const Gtk = imports.gi.Gtk;
21
const Lang = imports.lang;
22
const Mainloop = imports.mainloop;
23
const Signals = imports.signals;
24
const GLib = imports.gi.GLib;
25
26
const Extension = imports.misc.extensionUtils.getCurrentExtension();
27
const AppIndicator = Extension.imports.appIndicator;
28
const StatusNotifierDispatcher = Extension.imports.statusNotifierDispatcher;
47 by Jonas Kümmerlin
Internal reorganization, mostly in the settings dialog.
29
const Interfaces = Extension.imports.interfaces;
30
31
const Config = Extension.imports.config;
32
const ShellConfig = imports.misc.config;
1 by Jonas Kuemmerlin
initial commit
33
34
// TODO: replace with org.freedesktop and /org/freedesktop when approved
35
const KDE_PREFIX = 'org.kde';
36
const AYATANA_PREFIX = 'org.ayatana';
37
const AYATANA_PATH_PREFIX = '/org/ayatana';
38
39
const WATCHER_BUS_NAME = KDE_PREFIX + '.StatusNotifierWatcher';
40
const WATCHER_INTERFACE = WATCHER_BUS_NAME;
41
const WATCHER_OBJECT = '/StatusNotifierWatcher';
42
43
const ITEM_OBJECT = '/StatusNotifierItem';
44
40 by Jonas Kuemmerlin
Inline documentation updates everywhere
45
/*
46
 * The StatusNotifierWatcher class implements the StatusNotifierWatcher dbus object
47
 */
48
const StatusNotifierWatcher = new Lang.Class({
49
    Name: 'StatusNotifierWatcher',
47 by Jonas Kümmerlin
Internal reorganization, mostly in the settings dialog.
50
1 by Jonas Kuemmerlin
initial commit
51
    _init: function() {
47 by Jonas Kümmerlin
Internal reorganization, mostly in the settings dialog.
52
        this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(Interfaces.StatusNotifierWatcher, this);
1 by Jonas Kuemmerlin
initial commit
53
        this._dbusImpl.export(Gio.DBus.session, WATCHER_OBJECT);
54
        this._everAcquiredName = false;
3 by Jonas Kuemmerlin
call g_bus_unown_name on disable
55
        this._ownName = Gio.DBus.session.own_name(WATCHER_BUS_NAME,
1 by Jonas Kuemmerlin
initial commit
56
                                  Gio.BusNameOwnerFlags.NONE,
57
                                  Lang.bind(this, this._acquiredName),
58
                                  Lang.bind(this, this._lostName));
59
        this._items = { };
60
        this._nameWatcher = { };
61
    },
62
63
    _acquiredName: function() {
64
        this._everAcquiredName = true;
65
    },
66
67
    _lostName: function() {
68
        if (this._everAcquiredName)
13 by Jonas Kuemmerlin
debounce enable/disable operations. fixes #2
69
            log('appindicator: Lost name' + WATCHER_BUS_NAME);
1 by Jonas Kuemmerlin
initial commit
70
        else {
13 by Jonas Kuemmerlin
debounce enable/disable operations. fixes #2
71
            log('appindicator: Failed to acquire ' + WATCHER_BUS_NAME);
1 by Jonas Kuemmerlin
initial commit
72
        }
73
    },
47 by Jonas Kümmerlin
Internal reorganization, mostly in the settings dialog.
74
75
40 by Jonas Kuemmerlin
Inline documentation updates everywhere
76
    // create a unique index for the _items dictionary
1 by Jonas Kuemmerlin
initial commit
77
    _getItemId: function(bus_name, obj_path) {
47 by Jonas Kümmerlin
Internal reorganization, mostly in the settings dialog.
78
        return bus_name + obj_path;
1 by Jonas Kuemmerlin
initial commit
79
    },
80
81
    RegisterStatusNotifierItemAsync: function(params, invocation) {
82
        // it would be too easy if all application behaved the same
83
        // instead, ayatana patched gnome apps to send a path
84
        // while kde apps send a bus name
85
        let service = params[0];
86
        let bus_name, obj_path;
40 by Jonas Kuemmerlin
Inline documentation updates everywhere
87
        if (service.charAt(0) == '/') { // looks like a path
1 by Jonas Kuemmerlin
initial commit
88
            bus_name = invocation.get_sender();
89
            obj_path = service;
90
        } else { // we hope it is a bus name
91
            bus_name = service;
92
            obj_path = ITEM_OBJECT;
93
        }
94
95
        let id = this._getItemId(bus_name, obj_path);
47 by Jonas Kümmerlin
Internal reorganization, mostly in the settings dialog.
96
1 by Jonas Kuemmerlin
initial commit
97
        if(this._items[id]) {
98
            //delete the old one and add the new indicator
99
            log("WARNING: Attempting to re-register "+id+"; resetting instead");
100
            this._items[id].reset();
101
        } else {
102
            log("registering "+id+" for the first time.");
103
            this._items[id] = new AppIndicator.AppIndicator(bus_name, obj_path);
24 by Jonas Kuemmerlin
changed GVariant type from 's' to '(s)' when emitting DBus signals on StatusNotifierWatcher. Fixes annoying 'assertion failed' warnings.
104
            this._dbusImpl.emit_signal('ServiceRegistered', GLib.Variant.new('(s)', service));
1 by Jonas Kuemmerlin
initial commit
105
            this._nameWatcher[id] = Gio.DBus.session.watch_name(bus_name, Gio.BusNameWatcherFlags.NONE, null,
106
                                        Lang.bind(this, this._itemVanished));
107
            StatusNotifierDispatcher.IndicatorDispatcher.instance.dispatch(this._items[id]);
108
            this._dbusImpl.emit_property_changed('RegisteredStatusNotifierItems', null); //FIXME: null is incorrect
109
        }
110
        invocation.return_value(null);
111
    },
112
113
    _itemVanished: function(proxy, bus_name) {
114
        // FIXME: this is useless if the path name disappears while the bus stays alive (not unheard of)
115
        for (var i in this._items) {
116
            if (i.indexOf(bus_name) == 0) {
117
                this._remove(i);
118
            }
119
        }
120
    },
47 by Jonas Kümmerlin
Internal reorganization, mostly in the settings dialog.
121
1 by Jonas Kuemmerlin
initial commit
122
    _remove: function(id) {
29 by Jonas Kuemmerlin
replacing all tabs with 4 spaces
123
        this._items[id].destroy();
1 by Jonas Kuemmerlin
initial commit
124
        delete this._items[id];
125
        Gio.DBus.session.unwatch_name(this._nameWatcher[id]);
126
        delete this._nameWatcher[id];
24 by Jonas Kuemmerlin
changed GVariant type from 's' to '(s)' when emitting DBus signals on StatusNotifierWatcher. Fixes annoying 'assertion failed' warnings.
127
        this._dbusImpl.emit_signal('ServiceUnregistered', GLib.Variant.new('(s)', id));
1 by Jonas Kuemmerlin
initial commit
128
        this._dbusImpl.emit_property_changed('RegisteredStatusNotifierItems', null);
129
    },
47 by Jonas Kümmerlin
Internal reorganization, mostly in the settings dialog.
130
1 by Jonas Kuemmerlin
initial commit
131
    RegisterNotificationHost: function(service) {
132
        throw new Gio.DBusError('org.gnome.Shell.UnsupportedMethod',
133
                        'Registering additional notification hosts is not supported');
134
    },
135
136
    IsNotificationHostRegistered: function() {
137
        return true;
138
    },
139
140
    ProtocolVersion: function() {
141
        // "The version of the protocol the StatusNotifierWatcher instance implements." [sic]
142
        // in what syntax?
47 by Jonas Kümmerlin
Internal reorganization, mostly in the settings dialog.
143
        return "%s/%s (KDE; compatible; mostly) GNOME Shell/%s".format(Config.id, Config.version, ShellConfig.PACKAGE_VERSION);
1 by Jonas Kuemmerlin
initial commit
144
    },
145
146
    get RegisteredStatusNotifierItems() {
147
        return Object.keys(this._items);
148
    },
47 by Jonas Kümmerlin
Internal reorganization, mostly in the settings dialog.
149
1 by Jonas Kuemmerlin
initial commit
150
    get IsStatusNotifierHostRegistered() {
151
        return true;
152
    },
47 by Jonas Kümmerlin
Internal reorganization, mostly in the settings dialog.
153
1 by Jonas Kuemmerlin
initial commit
154
    destroy: function() {
29 by Jonas Kuemmerlin
replacing all tabs with 4 spaces
155
        if (!this._isDestroyed) {
40 by Jonas Kuemmerlin
Inline documentation updates everywhere
156
            // this doesn't do any sync operation and doesn't allow us to hook up the event of being finished
157
            // which results in our unholy debounce hack (see extension.js)
29 by Jonas Kuemmerlin
replacing all tabs with 4 spaces
158
            Gio.DBus.session.unown_name(this._ownName);
159
            this._dbusImpl.unexport();
160
            for (var i in this._nameWatcher) {
161
                Gio.DBus.session.unwatch_name(this._nameWatcher[i]);
162
            }
163
            delete this._nameWatcher;
164
            for (var i in this._items) {
165
                this._items[i].destroy();
166
            }
167
            delete this._items;
168
            this._isDestroyed = true;
169
        }
1 by Jonas Kuemmerlin
initial commit
170
    }
40 by Jonas Kuemmerlin
Inline documentation updates everywhere
171
});