-
Notifications
You must be signed in to change notification settings - Fork 92
/
panelWidget.js
326 lines (286 loc) · 11.4 KB
/
panelWidget.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
/*
This file is part of 'hamster-shell-extension'.
'hamster-shell-extension' 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 3 of the License, or
(at your option) any later version.
'hamster-shell-extension' 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 'hamster-shell-extension'. If not, see <http://www.gnu.org/licenses/>.
Copyright (c) 2011 Jerome Oufella <[email protected]>
Copyright (c) 2011-2012 Toms Baugis <[email protected]>
Icons Artwork Copyright (c) 2012 Reda Lazri <[email protected]>
Copyright (c) 2016 - 2018 Eric Goller / projecthamster <[email protected]>
*/
const Lang = imports.lang;
const Gio = imports.gi.Gio;
const Clutter = imports.gi.Clutter;
const PanelMenu = imports.ui.panelMenu;
const St = imports.gi.St;
const PopupMenu = imports.ui.popupMenu;
const GLib = imports.gi.GLib;
const Gettext = imports.gettext.domain('hamster-shell-extension');
const _ = Gettext.gettext;
const Me = imports.misc.extensionUtils.getCurrentExtension();
const FactsBox = Me.imports.widgets.factsBox.FactsBox;
const Stuff = Me.imports.stuff;
/**
* Class that defines the actual extension widget to be shown in the panel.
*
* -------------------------------------------------------------
* | Gnome top menu bar | PanelWidget / PanelWidget.panelLabel|
* -------------------------------------------------------------
* |PanelWidget.menu |
* | |
* |PanelWidget.factsBox |
* | |
* |PanelWidget.overviewMenuItem |
* |PanelWidget.stopTrackingMenuItem |
* |PanelWidget.addNewFactMenuItem |
* |PanelWidget.SettingMenuItem |
* | |
* ---------------------------------------
*
* @class
*/
var PanelWidget = new Lang.Class({
Name: 'PanelWidget',
Extends: PanelMenu.Button,
_init: function(controller) {
// [FIXME]
// What is the parameter?
this.parent(0.0);
this._controller = controller;
// [FIXME]
// Still needed?
this._extensionMeta = controller.extensionMeta;
this._settings = controller.settings;
this._windowsProxy = controller.windowsProxy;
controller.apiProxy.connectSignal('FactsChanged', Lang.bind(this, this.refresh));
controller.apiProxy.connectSignal('TagsChanged', Lang.bind(this, this.refresh));
// Setup the main layout container for the part of the extension
// visible in the panel.
let panelContainer = new St.BoxLayout({style_class: "panel-box"});
this.actor.add_actor(panelContainer);
this.actor.add_style_class_name('panel-status-button');
this.panelLabel = new St.Label({
text: _("Loading..."),
y_align: Clutter.ActorAlign.CENTER
});
// If we want to switch icons, we actually keep the same instance and
// just swap the actual image.
// [FIXME]
// Is there no path manipulation lib?
this._trackingIcon = Gio.icon_new_for_string(this._extensionMeta.path + "/images/hamster-tracking-symbolic.svg");
this._idleIcon = Gio.icon_new_for_string(this._extensionMeta.path + "/images/hamster-idle-symbolic.svg");
this.icon = new St.Icon({gicon: this._trackingIcon,
icon_size: 16,
style_class: "panel-icon"});
panelContainer.add(this.icon);
panelContainer.add(this.panelLabel);
this.factsBox = new FactsBox(controller, this);
this.menu.addMenuItem(this.factsBox);
// overview
let overviewMenuItem = new PopupMenu.PopupMenuItem(_("Show Overview"));
overviewMenuItem.connect('activate', Lang.bind(this, this._onOpenOverview));
this.menu.addMenuItem(overviewMenuItem);
// [FIXME]
// This should only be shown if we have an 'ongoing fact'.
// stop tracking
let stopTrackinMenuItem = new PopupMenu.PopupMenuItem(_("Stop Tracking"));
stopTrackinMenuItem.connect('activate', Lang.bind(this, this._onStopTracking));
this.menu.addMenuItem(stopTrackinMenuItem);
// add new task
let addNewFactMenuItem = new PopupMenu.PopupMenuItem(_("Add Earlier Activity"));
addNewFactMenuItem.connect('activate', Lang.bind(this, this._onOpenAddFact));
this.menu.addMenuItem(addNewFactMenuItem);
// settings
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
let SettingMenuItem = new PopupMenu.PopupMenuItem(_("Tracking Settings"));
SettingMenuItem.connect('activate', Lang.bind(this, this._onOpenSettings));
this.menu.addMenuItem(SettingMenuItem);
// focus menu upon display
this.menu.connect('open-state-changed', Lang.bind(this,
function(menu, open) {
if (open) {
this.factsBox.focus();
} else {
this.factsBox.unfocus();
}
}
));
// refresh the widget every 60 secs
this.timeout = GLib.timeout_add_seconds(0, 60, Lang.bind(this, this.refresh));
this.connect('destroy', Lang.bind(this, this._disableRefreshTimer));
this.refresh();
},
/**
* This is our main 'update/refresh' method.
* Whenever we suspect that things have changed (via dbus signals)
* this should be triggered.
* Upon such triggering, this method should call relevant sub-widget's
* 'refresh' method.
* As such it should do as little work as possible and rather gather all
* required facts etc and pass them to the relevant sub-widget's
* refresh methods.
*/
refresh: function() {
/**
* We need to wrap our actual refresh code in this callback for now as
* I am having major difficulties using a syncronous dbus method call to
* fetch the array of 'todaysFacts'.
*/
function _refresh([response], err) {
/**
* Extract *ongoing fact* from our list of facts. Due to how *legacy
* hamster* works this is the first element in the array returned my
* ``getTodayFacts``.
*
* Returns ``null`` if there is no *ongoing fact*.
*/
function getOngoingFact(facts) {
let result = null;
if (facts.length) {
let lastFact = facts[facts.length - 1];
if (!lastFact.endTime) { result = lastFact; }
}
return result;
}
let facts = [];
// [FIXME]
// This seems a rather naive way to handle potential errors.
if (err) {
log(err);
} else if (response.length > 0) {
facts = Stuff.fromDbusFacts(response);
}
let ongoingFact = getOngoingFact(facts);
this.updatePanelDisplay(ongoingFact);
this.factsBox.refresh(facts, ongoingFact);
}
// [FIXME]
// This should really be a synchronous call fetching the facts.
// Once this is done, the actual code from the callback should follow
// here.
this._controller.apiProxy.GetTodaysFactsRemote(Lang.bind(this, _refresh));
return GLib.SOURCE_CONTINUE;
},
/**
* Open 'popup menu' containing the bulk of the extension widgets.
*/
show: function() {
this.menu.open();
},
/**
* Close/Open the 'popup menu' depending on previous state.
*/
toggle: function() {
this.menu.toggle();
},
/**
* Update the rendering of the PanelWidget in the panel itself.
*
* Depending on the 'display mode' set in the extensions settings this has
* slightly different consequences.
*/
updatePanelDisplay: function(fact) {
/**
* Return a text string representing the passed fact suitable for the panelLabel.
*
* @param fact The fact to be represented. Be advised. If there is no
* *ongoing fact* this will be ``null``!
*/
function getLabelString(fact) {
let result = _("No activity");
if (fact) {
result = "%s %s".format(fact.name, Stuff.formatDuration(fact.delta));
}
return result;
}
/**
* Returns the appropriate icon image depending on ``fact``.
*/
function getIcon(panelWidget) {
let result = panelWidget._idleIcon;
if (fact) { result = panelWidget._trackingIcon; }
return result;
}
// 0 = show label, 1 = show just icon, 2 = show label and icon
switch (this._settings.get_int("panel-appearance")) {
case 0:
this.panelLabel.set_text(getLabelString(fact));
this.panelLabel.show();
this.icon.hide();
break;
case 1:
this.icon.gicon = getIcon(this);
this.icon.show();
this.panelLabel.hide();
break;
case 2:
this.icon.gicon = getIcon(this);
this.icon.show();
this.panelLabel.set_text(getLabelString(fact));
this.panelLabel.show();
break;
}
},
/**
* Disable the refresh timer.
*
* @callback panelWidget~_disableRefreshTimer
*
* This method is actually a callback triggered on the destroy
* signal.
*/
_disableRefreshTimer: function() {
GLib.source_remove(this.timeout);
},
/**
* Callback to be triggered when an *ongoing fact* is stopped.
* @callback PanelWidget~_onStopTracking
*
* This will get the current time and issue the ``StopTracking``
* method call to the dbus interface.
*/
_onStopTracking: function() {
let now = new Date();
let epochSeconds = Date.UTC(now.getFullYear(),
now.getMonth(),
now.getDate(),
now.getHours(),
now.getMinutes(),
now.getSeconds());
epochSeconds = Math.floor(epochSeconds / 1000);
this._controller.apiProxy.StopTrackingRemote(GLib.Variant.new('i', [epochSeconds]));
},
/**
* Callback that triggers opening of the *Overview*-Window.
*
* @callback panelWidget~_onOpenOverview
*/
_onOpenOverview: function() {
this._controller.windowsProxy.overviewSync();
},
/**
* Callback that triggers opening of the *Add Fact*-Window.
*
* @callback panelWidget~_onOpenAddFact
*/
_onOpenAddFact: function() {
this._controller.windowsProxy.editSync(GLib.Variant.new('i', [0]));
},
/**
* Callback that triggers opening of the *Add Fact*-Window.
*
* @callback panelWidget~_onOpenSettings
*
* Note: This will open the GUI settings, not the extension settings!
*/
_onOpenSettings: function() {
this._controller.windowsProxy.preferencesSync();
},
});