32 | 32 |
const Util = Extension.imports.util;
|
33 | 33 |
const Interfaces = Extension.imports.interfaces;
|
34 | 34 |
|
35 | |
const DEFAULT_FALLBACK_ICON_SIZE = 22
|
36 | |
|
37 | 35 |
const SNICategory = {
|
38 | 36 |
APPLICATION: 'ApplicationStatus',
|
39 | 37 |
COMMUNICATIONS: 'Communications',
|
|
47 | 45 |
NEEDS_ATTENTION: 'NeedsAttention'
|
48 | 46 |
};
|
49 | 47 |
|
|
48 |
const SNIconType = {
|
|
49 |
NORMAL: 0,
|
|
50 |
ATTENTION: 1,
|
|
51 |
OVERLAY: 2,
|
|
52 |
};
|
|
53 |
|
50 | 54 |
/**
|
51 | 55 |
* the AppIndicator class serves as a generic container for indicator information and functions common
|
52 | 56 |
* for every displaying implementation (IndicatorMessageSource and IndicatorStatusIcon)
|
|
61 | 65 |
|
62 | 66 |
//HACK: we cannot use Gio.DBusProxy.makeProxyWrapper because we need
|
63 | 67 |
// to specify G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES
|
|
68 |
this._cancellable = new Gio.Cancellable();
|
64 | 69 |
this._proxy = new Gio.DBusProxy({ g_connection: Gio.DBus.session,
|
65 | 70 |
g_interface_name: interface_info.name,
|
66 | 71 |
g_interface_info: interface_info,
|
67 | 72 |
g_name: bus_name,
|
68 | 73 |
g_object_path: object,
|
69 | 74 |
g_flags: Gio.DBusProxyFlags.GET_INVALIDATED_PROPERTIES })
|
70 | |
this._proxy.init_async(GLib.PRIORITY_DEFAULT, null, ((initable, result) => {
|
|
75 |
this._proxy.init_async(GLib.PRIORITY_DEFAULT, this._cancellable, ((initable, result) => {
|
71 | 76 |
try {
|
72 | 77 |
initable.init_finish(result);
|
73 | 78 |
this._checkIfReady();
|
|
80 | 85 |
});
|
81 | 86 |
}
|
82 | 87 |
} catch(e) {
|
83 | |
Util.Logger.warn("While intializing proxy for "+bus_name+object+": "+e)
|
|
88 |
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
|
|
89 |
Util.Logger.warn(`While intializing proxy for ${bus_name} ${object}: ${e}`);
|
84 | 90 |
}
|
85 | 91 |
}))
|
86 | 92 |
|
|
245 | 251 |
this.emit('destroy')
|
246 | 252 |
|
247 | 253 |
this.disconnectAll()
|
|
254 |
this._cancellable.cancel();
|
|
255 |
Util.cancelRefreshPropertyOnProxy(this._proxy);
|
|
256 |
delete this._cancellable;
|
248 | 257 |
delete this._proxy
|
249 | 258 |
|
250 | 259 |
if (this._delayCheck) {
|
|
276 | 285 |
Signals.addSignalMethods(AppIndicator.prototype);
|
277 | 286 |
|
278 | 287 |
var IconActor = GObject.registerClass(
|
279 | |
class AppIndicators_IconActor extends Shell.Stack {
|
|
288 |
class AppIndicators_IconActor extends St.Icon {
|
280 | 289 |
|
281 | 290 |
_init(indicator, icon_size) {
|
282 | |
super._init({ reactive: true })
|
|
291 |
super._init({
|
|
292 |
reactive: true,
|
|
293 |
style_class: 'system-status-icon',
|
|
294 |
fallback_icon_name: 'image-loading-symbolic',
|
|
295 |
});
|
|
296 |
|
283 | 297 |
this.name = this.constructor.name;
|
|
298 |
this.add_style_class_name('appindicator-icon');
|
284 | 299 |
|
285 | 300 |
let themeContext = St.ThemeContext.get_for_stage(global.stage);
|
286 | 301 |
this.height = icon_size * themeContext.scale_factor;
|
|
288 | 303 |
this._indicator = indicator
|
289 | 304 |
this._iconSize = icon_size
|
290 | 305 |
this._iconCache = new IconCache.IconCache()
|
291 | |
|
292 | |
this._mainIcon = new St.Bin({ style: 'padding: 2px 0 2px 0;'})
|
293 | |
this._overlayIcon = new St.Bin({ 'x-align': St.Align.END, 'y-align': St.Align.END })
|
294 | |
|
295 | |
this.add_actor(this._mainIcon)
|
296 | |
this.add_actor(this._overlayIcon)
|
|
306 |
this._cancellable = new Gio.Cancellable();
|
|
307 |
this._loadingIcons = new Set();
|
297 | 308 |
|
298 | 309 |
Util.connectSmart(this._indicator, 'icon', this, '_updateIcon')
|
299 | 310 |
Util.connectSmart(this._indicator, 'overlay-icon', this, '_updateOverlayIcon')
|
300 | |
Util.connectSmart(this._indicator, 'ready', this, '_invalidateIcon')
|
301 | 311 |
Util.connectSmart(this._indicator, 'reset', this, '_invalidateIcon')
|
302 | 312 |
Util.connectSmart(this, 'scroll-event', this, '_handleScrollEvent')
|
303 | 313 |
|
304 | 314 |
Util.connectSmart(themeContext, 'notify::scale-factor', this, (tc) => {
|
305 | 315 |
this.height = icon_size * tc.scale_factor;
|
306 | |
this._updateIcon();
|
307 | |
this._updateOverlayIcon();
|
|
316 |
this._invalidateIcon();
|
308 | 317 |
});
|
|
318 |
|
|
319 |
Util.connectSmart(this._indicator, 'ready', this, () => {
|
|
320 |
this._updateIconClass();
|
|
321 |
this._invalidateIcon();
|
|
322 |
})
|
309 | 323 |
|
310 | 324 |
Util.connectSmart(Gtk.IconTheme.get_default(), 'changed', this, '_invalidateIcon')
|
311 | 325 |
|
|
314 | 328 |
|
315 | 329 |
this.connect('destroy', () => {
|
316 | 330 |
this._iconCache.destroy();
|
|
331 |
this._cancellable.cancel();
|
317 | 332 |
});
|
|
333 |
}
|
|
334 |
|
|
335 |
_updateIconClass() {
|
|
336 |
this.add_style_class_name(
|
|
337 |
`appindicator-icon-${this._indicator.id.toLowerCase().replace(/_|\s/g, '-')}`);
|
318 | 338 |
}
|
319 | 339 |
|
320 | 340 |
// Will look the icon up in the cache, if it's found
|
|
323 | 343 |
// the returned icon anymore, make sure to check the .inUse property
|
324 | 344 |
// and set it to false if needed so that it can be picked up by the garbage
|
325 | 345 |
// collector.
|
326 | |
_cacheOrCreateIconByName(iconSize, iconName, themePath) {
|
327 | |
let themeContext = St.ThemeContext.get_for_stage(global.stage);
|
328 | |
iconSize *= themeContext.scale_factor;
|
329 | |
let id = iconName + '@' + iconSize + (themePath ? '##' + themePath : '');
|
330 | |
let icon = this._iconCache.get(id);
|
331 | |
|
332 | |
if (!icon) {
|
333 | |
let [path,] = this._getIconInfo(iconName, themePath, iconSize);
|
334 | |
icon = this._createIconByName(path, iconSize);
|
335 | |
}
|
336 | |
|
337 | |
if (icon) {
|
338 | |
icon.inUse = true;
|
339 | |
this._iconCache.add(id, icon);
|
340 | |
}
|
341 | |
|
342 | |
return icon;
|
343 | |
}
|
344 | |
|
345 | |
_createIconByName(path, realSize) {
|
346 | |
let icon = null;
|
347 | |
try {
|
348 | |
let pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_scale(path, -1, realSize, true);
|
349 | |
icon = new St.Icon({
|
350 | |
gicon: pixbuf
|
351 | |
});
|
352 | |
icon.set_icon_size(Math.max(pixbuf.get_width(), pixbuf.get_height()));
|
353 | |
icon.set_width(pixbuf.get_width());
|
354 | |
icon.set_height(pixbuf.get_height());
|
355 | |
} catch (e) {
|
356 | |
Util.Logger.warn(`Impossible to create image from path '${path}': ${e}`)
|
357 | |
}
|
358 | |
return icon;
|
359 | |
}
|
360 | |
|
361 | |
_getIconInfo(name, themePath, size) {
|
362 | |
if (!size)
|
363 | |
size = DEFAULT_FALLBACK_ICON_SIZE;
|
364 | |
// realSize will contain the actual icon size in contrast to the requested icon size.
|
365 | |
let realSize = size;
|
|
346 |
_cacheOrCreateIconByName(iconSize, iconName, themePath, callback) {
|
|
347 |
let {scale_factor} = St.ThemeContext.get_for_stage(global.stage);
|
|
348 |
let id = `${iconName}@${iconSize * scale_factor}${themePath || ''}`;
|
|
349 |
let gicon = this._iconCache.get(id);
|
|
350 |
|
|
351 |
if (gicon) {
|
|
352 |
callback(gicon);
|
|
353 |
return;
|
|
354 |
}
|
|
355 |
|
|
356 |
if (this._loadingIcons.has(id)) {
|
|
357 |
Util.Logger.debug(`${this._indicator.id}, Icon ${id} Is still loading, ignoring the request`);
|
|
358 |
return;
|
|
359 |
} else if (this._loadingIcons.size > 0) {
|
|
360 |
this._cancellable.cancel();
|
|
361 |
this._cancellable = new Gio.Cancellable();
|
|
362 |
this._loadingIcons.clear();
|
|
363 |
}
|
|
364 |
|
|
365 |
this._loadingIcons.add(id);
|
|
366 |
let path = this._getIconInfo(iconName, themePath, iconSize, scale_factor);
|
|
367 |
this._createIconByName(path, (gicon) => {
|
|
368 |
this._loadingIcons.delete(id);
|
|
369 |
if (gicon) {
|
|
370 |
gicon.inUse = true;
|
|
371 |
this._iconCache.add(id, gicon);
|
|
372 |
}
|
|
373 |
callback(gicon);
|
|
374 |
});
|
|
375 |
}
|
|
376 |
|
|
377 |
_createIconByPath(path, width, height, callback) {
|
|
378 |
let file = Gio.File.new_for_path(path);
|
|
379 |
file.read_async(GLib.PRIORITY_DEFAULT, this._cancellable, (file, res) => {
|
|
380 |
try {
|
|
381 |
let inputStream = file.read_finish(res);
|
|
382 |
|
|
383 |
GdkPixbuf.Pixbuf.new_from_stream_at_scale_async(
|
|
384 |
inputStream, height, width, true, this._cancellable, (_p, res) => {
|
|
385 |
try {
|
|
386 |
callback(GdkPixbuf.Pixbuf.new_from_stream_finish(res));
|
|
387 |
this.icon_size = width > 0 ? width : this._iconSize;
|
|
388 |
} catch (e) {
|
|
389 |
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
|
390 |
Util.Logger.warn(`${this._indicator.id}, Impossible to create image from path '${path}': ${e}`);
|
|
391 |
callback(null);
|
|
392 |
}
|
|
393 |
}
|
|
394 |
});
|
|
395 |
} catch (e) {
|
|
396 |
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
|
397 |
Util.Logger.warn(`${this._indicator.id}, Impossible to read image from path '${path}': ${e}`);
|
|
398 |
callback(null);
|
|
399 |
}
|
|
400 |
}
|
|
401 |
});
|
|
402 |
}
|
|
403 |
|
|
404 |
_createIconByName(path, callback) {
|
|
405 |
GdkPixbuf.Pixbuf.get_file_info_async(path, this._cancellable, (_p, res) => {
|
|
406 |
try {
|
|
407 |
let [format, width, height] = GdkPixbuf.Pixbuf.get_file_info_finish(res);
|
|
408 |
|
|
409 |
if (!format) {
|
|
410 |
Util.Logger.critical(`${this._indicator.id}, Invalid image format: ${path}`);
|
|
411 |
callback(null);
|
|
412 |
return;
|
|
413 |
}
|
|
414 |
|
|
415 |
if (width >= height * 1.5) {
|
|
416 |
/* Hello indicator-multiload! */
|
|
417 |
this._createIconByPath(path, width, -1, callback);
|
|
418 |
} else {
|
|
419 |
callback(new Gio.FileIcon({
|
|
420 |
file: Gio.File.new_for_path(path)
|
|
421 |
}));
|
|
422 |
this.icon_size = this._iconSize;
|
|
423 |
}
|
|
424 |
} catch (e) {
|
|
425 |
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
|
426 |
Util.Logger.warn(`${this._indicator.id}, Impossible to read image info from path '${path}': ${e}`);
|
|
427 |
callback(null);
|
|
428 |
}
|
|
429 |
}
|
|
430 |
});
|
|
431 |
}
|
|
432 |
|
|
433 |
_getIconInfo(name, themePath, size, scale) {
|
366 | 434 |
let path = null;
|
367 | 435 |
if (name && name[0] == "/") {
|
368 | 436 |
//HACK: icon is a path name. This is not specified by the api but at least inidcator-sensors uses it.
|
369 | |
let [ format, width, height ] = GdkPixbuf.Pixbuf.get_file_info(name);
|
370 | |
if (!format) {
|
371 | |
Util.Logger.critical("invalid image format: " + name);
|
372 | |
} else {
|
373 | |
// if the actual icon size is smaller, save that for later.
|
374 | |
// scaled icons look ugly.
|
375 | |
if (Math.max(width, height) < size)
|
376 | |
realSize = Math.max(width, height);
|
377 | |
path = name;
|
378 | |
}
|
|
437 |
path = name;
|
379 | 438 |
} else if (name) {
|
380 | 439 |
// we manually look up the icon instead of letting st.icon do it for us
|
381 | 440 |
// this allows us to sneak in an indicator provided search path and to avoid ugly upscaled icons
|
|
400 | 459 |
}
|
401 | 460 |
if (icon_theme) {
|
402 | 461 |
// try to look up the icon in the icon theme
|
403 | |
iconInfo = icon_theme.lookup_icon(name, size,
|
404 | |
Gtk.IconLookupFlags.GENERIC_FALLBACK);
|
|
462 |
iconInfo = icon_theme.lookup_icon_for_scale(name, size, scale,
|
|
463 |
Gtk.IconLookupFlags.GENERIC_FALLBACK);
|
405 | 464 |
// no icon? that's bad!
|
406 | 465 |
if (iconInfo === null) {
|
407 | |
Util.Logger.fatal("unable to lookup icon for " + name);
|
|
466 |
Util.Logger.warn(`${this._indicator.id}, Impossible to lookup icon for '${name}'`);
|
408 | 467 |
} else { // we have an icon
|
409 | |
// the icon size may not match the requested size, especially with custom themes
|
410 | |
if (iconInfo.get_base_size() < size) {
|
411 | |
// stretched icons look very ugly, we avoid that and just show the smaller icon
|
412 | |
realSize = iconInfo.get_base_size();
|
413 | |
}
|
414 | 468 |
// get the icon path
|
415 | 469 |
path = iconInfo.get_filename();
|
416 | 470 |
}
|
417 | 471 |
}
|
418 | 472 |
}
|
419 | |
return [path, realSize];
|
420 | |
}
|
421 | |
|
422 | |
_createIconFromPixmap(iconSize, iconPixmapArray) {
|
423 | |
let themeContext = St.ThemeContext.get_for_stage(global.stage);
|
424 | |
let scale_factor = themeContext.scale_factor;
|
|
473 |
return path;
|
|
474 |
}
|
|
475 |
|
|
476 |
argbToRgba(src) {
|
|
477 |
let dest = new Uint8Array(src.length);
|
|
478 |
|
|
479 |
for (let i = 0; i < src.length; i += 4) {
|
|
480 |
let srcAlpha = src[i]
|
|
481 |
|
|
482 |
dest[i] = src[i + 1]; /* red */
|
|
483 |
dest[i + 1] = src[i + 2]; /* green */
|
|
484 |
dest[i + 2] = src[i + 3]; /* blue */
|
|
485 |
dest[i + 3] = srcAlpha; /* alpha */
|
|
486 |
}
|
|
487 |
|
|
488 |
return dest;
|
|
489 |
}
|
|
490 |
|
|
491 |
_createIconFromPixmap(iconSize, iconPixmapArray, snIconType) {
|
|
492 |
let {scale_factor} = St.ThemeContext.get_for_stage(global.stage);
|
425 | 493 |
iconSize = iconSize * scale_factor
|
426 | 494 |
// the pixmap actually is an array of pixmaps with different sizes
|
427 | 495 |
// we use the one that is smaller or equal the iconSize
|
|
431 | 499 |
return null
|
432 | 500 |
|
433 | 501 |
let sortedIconPixmapArray = iconPixmapArray.sort((pixmapA, pixmapB) => {
|
434 | |
// we sort biggest to smallest
|
|
502 |
// we sort smallest to biggest
|
435 | 503 |
let areaA = pixmapA[0] * pixmapA[1]
|
436 | 504 |
let areaB = pixmapB[0] * pixmapB[1]
|
437 | 505 |
|
438 | |
return areaB - areaA
|
|
506 |
return areaA - areaB
|
439 | 507 |
})
|
440 | 508 |
|
441 | 509 |
let qualifiedIconPixmapArray = sortedIconPixmapArray.filter((pixmap) => {
|
442 | |
// we disqualify any pixmap that is bigger than our requested size
|
443 | |
return pixmap[0] <= iconSize && pixmap[1] <= iconSize
|
|
510 |
// we prefer any pixmap that is equal or bigger than our requested size
|
|
511 |
return pixmap[0] >= iconSize && pixmap[1] >= iconSize;
|
444 | 512 |
})
|
445 | 513 |
|
446 | |
// if no one got qualified, we use the smallest one available
|
447 | 514 |
let iconPixmap = qualifiedIconPixmapArray.length > 0 ? qualifiedIconPixmapArray[0] : sortedIconPixmapArray.pop()
|
448 | 515 |
|
449 | 516 |
let [ width, height, bytes ] = iconPixmap
|
450 | 517 |
let rowstride = width * 4 // hopefully this is correct
|
451 | 518 |
|
452 | 519 |
try {
|
453 | |
let image = new Clutter.Image()
|
454 | |
image.set_bytes(bytes,
|
455 | |
Cogl.PixelFormat.ARGB_8888,
|
456 | |
width,
|
457 | |
height,
|
458 | |
rowstride)
|
459 | |
|
460 | |
let scale_factor = themeContext.scale_factor;
|
461 | |
if (height != 0)
|
462 | |
scale_factor = iconSize / height
|
463 | |
|
464 | |
const PivotPoint = Clutter.Point || imports.gi.Graphene.Point;
|
465 | |
return new Clutter.Actor({
|
466 | |
width: Math.min(width, iconSize),
|
467 | |
height: Math.min(height, iconSize),
|
468 | |
content: image,
|
469 | |
scale_x: scale_factor,
|
470 | |
scale_y: scale_factor,
|
471 | |
pivot_point: new PivotPoint({ x: .5, y: .5 }),
|
472 | |
})
|
|
520 |
return GdkPixbuf.Pixbuf.new_from_bytes(
|
|
521 |
this.argbToRgba(bytes),
|
|
522 |
GdkPixbuf.Colorspace.RGB, true,
|
|
523 |
8, width, height, rowstride);
|
473 | 524 |
} catch (e) {
|
474 | 525 |
// the image data was probably bogus. We don't really know why, but it _does_ happen.
|
475 | |
Util.Logger.debug(`Impossible to create image from data: ${e}`)
|
|
526 |
Util.Logger.warn(`${this._indicator.id}, Impossible to create image from data: ${e}`)
|
476 | 527 |
return null
|
477 | 528 |
}
|
|
529 |
}
|
|
530 |
|
|
531 |
_setGicon(iconType, gicon) {
|
|
532 |
if (iconType != SNIconType.OVERLAY) {
|
|
533 |
if (gicon) {
|
|
534 |
this.gicon = new Gio.EmblemedIcon({ gicon });
|
|
535 |
} else {
|
|
536 |
this.gicon = null;
|
|
537 |
Util.Logger.critical(`unable to update icon for ${this._indicator.id}`);
|
|
538 |
}
|
|
539 |
} else {
|
|
540 |
if (gicon) {
|
|
541 |
this._emblem = new Gio.Emblem({ icon: gicon });
|
|
542 |
} else {
|
|
543 |
this._emblem = null;
|
|
544 |
Util.Logger.debug(`unable to update icon emblem for ${this._indicator.id}`);
|
|
545 |
}
|
|
546 |
}
|
|
547 |
|
|
548 |
if (this.gicon) {
|
|
549 |
if (!this._emblem || !this.gicon.get_emblems().includes(this._emblem)) {
|
|
550 |
this.gicon.clear_emblems();
|
|
551 |
if (this._emblem)
|
|
552 |
this.gicon.add_emblem(this._emblem);
|
|
553 |
}
|
|
554 |
}
|
|
555 |
}
|
|
556 |
|
|
557 |
_updateIconByType(iconType, iconSize) {
|
|
558 |
let icon;
|
|
559 |
switch (iconType) {
|
|
560 |
case SNIconType.ATTENTION:
|
|
561 |
icon = this._indicator.attentionIcon;
|
|
562 |
break;
|
|
563 |
case SNIconType.NORMAL:
|
|
564 |
icon = this._indicator.icon;
|
|
565 |
break;
|
|
566 |
case SNIconType.OVERLAY:
|
|
567 |
icon = this._indicator.overlayIcon;
|
|
568 |
break;
|
|
569 |
}
|
|
570 |
|
|
571 |
let [name, pixmap, theme] = icon;
|
|
572 |
if (name && name.length) {
|
|
573 |
this._cacheOrCreateIconByName(iconSize, name, theme, (gicon) => {
|
|
574 |
if (!gicon && pixmap) {
|
|
575 |
gicon = this._createIconFromPixmap(iconSize,
|
|
576 |
pixmap, iconType);
|
|
577 |
}
|
|
578 |
this._setGicon(iconType, gicon);
|
|
579 |
});
|
|
580 |
} else if (pixmap) {
|
|
581 |
let gicon = this._createIconFromPixmap(iconSize,
|
|
582 |
pixmap, iconType);
|
|
583 |
this._setGicon(iconType, gicon);
|
|
584 |
}
|
478 | 585 |
}
|
479 | 586 |
|
480 | 587 |
// updates the base icon
|
481 | 588 |
_updateIcon() {
|
482 | |
// remove old icon
|
483 | |
if (this._mainIcon.get_child()) {
|
484 | |
let child = this._mainIcon.get_child()
|
485 | |
|
486 | |
if (child.inUse)
|
487 | |
child.inUse = false
|
488 | |
else if (child.destroy)
|
489 | |
child.destroy()
|
490 | |
|
491 | |
this._mainIcon.set_child(null)
|
492 | |
}
|
493 | |
|
494 | |
// place to save the new icon
|
495 | |
let newIcon = null
|
|
589 |
if (this.gicon) {
|
|
590 |
let { gicon } = this;
|
|
591 |
|
|
592 |
if (gicon.inUse)
|
|
593 |
gicon.inUse = false
|
|
594 |
}
|
496 | 595 |
|
497 | 596 |
// we might need to use the AttentionIcon*, which have precedence over the normal icons
|
498 | |
if (this._indicator.status == SNIStatus.NEEDS_ATTENTION) {
|
499 | |
let [ name, pixmap, theme ] = this._indicator.attentionIcon
|
500 | |
|
501 | |
if (name && name.length)
|
502 | |
newIcon = this._cacheOrCreateIconByName(this._iconSize, name, theme)
|
503 | |
|
504 | |
if (!newIcon && pixmap)
|
505 | |
newIcon = this._createIconFromPixmap(this._iconSize, pixmap)
|
506 | |
}
|
507 | |
|
508 | |
if (!newIcon) {
|
509 | |
let [ name, pixmap, theme ] = this._indicator.icon
|
510 | |
|
511 | |
if (name && name.length)
|
512 | |
newIcon = this._cacheOrCreateIconByName(this._iconSize, name, theme)
|
513 | |
|
514 | |
if (!newIcon && pixmap)
|
515 | |
newIcon = this._createIconFromPixmap(this._iconSize, pixmap)
|
516 | |
}
|
517 | |
|
518 | |
if (!newIcon) {
|
519 | |
Util.Logger.critical("unable to update icon");
|
520 | |
return;
|
521 | |
}
|
522 | |
|
523 | |
this._mainIcon.set_child(newIcon)
|
|
597 |
let iconType = this._indicator.status == SNIStatus.NEEDS_ATTENTION ?
|
|
598 |
SNIconType.ATTENTION : SNIconType.NORMAL;
|
|
599 |
|
|
600 |
this._updateIconByType(iconType, this._iconSize);
|
524 | 601 |
}
|
525 | 602 |
|
526 | 603 |
_updateOverlayIcon() {
|
527 | 604 |
// remove old icon
|
528 | |
if (this._overlayIcon.get_child()) {
|
529 | |
let child = this._overlayIcon.get_child()
|
530 | |
|
531 | |
if (child.inUse)
|
532 | |
child.inUse = false
|
533 | |
else if (child.destroy)
|
534 | |
child.destroy()
|
535 | |
|
536 | |
this._overlayIcon.set_child(null)
|
|
605 |
if (this.gicon && this.gicon.get_emblems().length) {
|
|
606 |
let [emblem] = this.gicon.get_emblems();
|
|
607 |
|
|
608 |
if (emblem.inUse)
|
|
609 |
emblem.inUse = false
|
537 | 610 |
}
|
538 | 611 |
|
539 | 612 |
// KDE hardcodes the overlay icon size to 10px (normal icon size 16px)
|
|
541 | 614 |
// our algorithms will always pick a smaller one instead of stretching it.
|
542 | 615 |
let iconSize = Math.floor(this._iconSize / 1.6)
|
543 | 616 |
|
544 | |
let newIcon = null
|
545 | |
|
546 | |
// create new
|
547 | |
let [ name, pixmap, theme ] = this._indicator.overlayIcon
|
548 | |
|
549 | |
if (name && name.length)
|
550 | |
newIcon = this._cacheOrCreateIconByName(iconSize, name, theme)
|
551 | |
|
552 | |
if (!newIcon && pixmap)
|
553 | |
newIcon = this._createIconFromPixmap(iconSize, pixmap)
|
554 | |
|
555 | |
this._overlayIcon.set_child(newIcon)
|
|
617 |
this._updateIconByType(SNIconType.OVERLAY, iconSize);
|
556 | 618 |
}
|
557 | 619 |
|
558 | 620 |
_handleScrollEvent(actor, event) {
|