[Pkg-mozext-commits] [firetray] 05/22: Further AppIndicator implementation: * Refactoring: split StatusIcon into GtkStatusIcon and AppIndicator. * Add AppIndicator-specific menu items (tooltip + ShowHide) * Show/hide on scroll. * Setting icon visibility.

David Prévot taffit at moszumanska.debian.org
Wed Dec 31 17:04:37 UTC 2014


This is an automated email from the git hooks/post-receive script.

taffit pushed a commit to branch master
in repository firetray.

commit 9deaea08bb1d25cdd4b6b978fc2868cce5f70e20
Author: foudfou <foudil.newbie+git at gmail.com>
Date:   Sun Nov 16 23:37:54 2014 +0100

    Further AppIndicator implementation:
    * Refactoring: split StatusIcon into GtkStatusIcon and AppIndicator.
    * Add AppIndicator-specific menu items (tooltip + ShowHide)
    * Show/hide on scroll.
    * Setting icon visibility.
    
    Due to appindicator's limitations, we'll need to abandon these:
    * Icon text (message count).
    * Themed icons (multiple icon names).
---
 src/chrome/locale/de-DE/overlay.properties         |   1 +
 src/chrome/locale/en-US/overlay.properties         |   1 +
 src/chrome/locale/es-AR/overlay.properties         |   1 +
 src/chrome/locale/fr/overlay.properties            |   1 +
 src/chrome/locale/hr-HR/overlay.properties         |   1 +
 src/chrome/locale/it/overlay.properties            |   1 +
 src/chrome/locale/nl/overlay.properties            |   1 +
 src/chrome/locale/pl-PL/overlay.properties         |   1 +
 src/chrome/locale/ru-RU/overlay.properties         |   1 +
 src/chrome/locale/sk-SK/overlay.properties         |   1 +
 src/chrome/locale/uk-UA/overlay.properties         |   1 +
 src/chrome/locale/zh-TW/overlay.properties         |   1 +
 src/modules/FiretrayHandler.jsm                    |   7 +-
 src/modules/ctypes/linux/appindicator.jsm          |  13 +-
 src/modules/ctypes/linux/gtk.jsm                   |   2 +
 src/modules/linux/FiretrayAppIndicator.jsm         | 136 ++++++++
 src/modules/linux/FiretrayChatStatusIcon.jsm       |   8 +-
 src/modules/linux/FiretrayGtkIcons.jsm             |   5 +-
 ...rayStatusIcon.jsm => FiretrayGtkStatusIcon.jsm} | 251 +++-----------
 src/modules/linux/FiretrayPopupMenu.jsm            |  87 +++--
 src/modules/linux/FiretrayStatusIcon.jsm           | 375 +++------------------
 src/modules/winnt/FiretrayStatusIcon.jsm           |   2 -
 22 files changed, 338 insertions(+), 560 deletions(-)

diff --git a/src/chrome/locale/de-DE/overlay.properties b/src/chrome/locale/de-DE/overlay.properties
index 6f6d54b..7f8d3e0 100644
--- a/src/chrome/locale/de-DE/overlay.properties
+++ b/src/chrome/locale/de-DE/overlay.properties
@@ -4,5 +4,6 @@ popupMenu.itemLabel.Preferences=Einstellungen
 popupMenu.itemLabel.Quit=Beenden
 popupMenu.itemLabel.NewWindow=Neues Fenster
 popupMenu.itemLabel.NewMessage=Neue Nachricht
+popupMenu.itemLabel.ShowHide=Show/Hide
 tooltip.unread_messages=#1 ungelesene Nachricht;#1 ungelesene Nachrichten
 tooltip.new_messages=Neue Nachrichten!
diff --git a/src/chrome/locale/en-US/overlay.properties b/src/chrome/locale/en-US/overlay.properties
index 77127d0..3ad0107 100644
--- a/src/chrome/locale/en-US/overlay.properties
+++ b/src/chrome/locale/en-US/overlay.properties
@@ -4,5 +4,6 @@ popupMenu.itemLabel.Preferences=Preferences
 popupMenu.itemLabel.Quit=Quit
 popupMenu.itemLabel.NewWindow=New window
 popupMenu.itemLabel.NewMessage=New message
+popupMenu.itemLabel.ShowHide=Show/Hide
 tooltip.unread_messages=#1 unread message;#1 unread messages
 tooltip.new_messages=New messages !
diff --git a/src/chrome/locale/es-AR/overlay.properties b/src/chrome/locale/es-AR/overlay.properties
index ffe4888..112bf61 100644
--- a/src/chrome/locale/es-AR/overlay.properties
+++ b/src/chrome/locale/es-AR/overlay.properties
@@ -4,5 +4,6 @@ popupMenu.itemLabel.Preferences=Preferences
 popupMenu.itemLabel.Quit=Salir
 popupMenu.itemLabel.NewWindow=Nueva ventana
 popupMenu.itemLabel.NewMessage=Nuevo mensaje
+popupMenu.itemLabel.ShowHide=Show/Hide
 tooltip.unread_messages=#1 mensaje no leido;#1 mensajes no leídos
 tooltip.new_messages=Nuevos mensajes !
diff --git a/src/chrome/locale/fr/overlay.properties b/src/chrome/locale/fr/overlay.properties
index 04252d9..47a7800 100644
--- a/src/chrome/locale/fr/overlay.properties
+++ b/src/chrome/locale/fr/overlay.properties
@@ -4,5 +4,6 @@ popupMenu.itemLabel.Preferences=Préférences
 popupMenu.itemLabel.Quit=Quitter
 popupMenu.itemLabel.NewWindow=Nouvelle fenêtre
 popupMenu.itemLabel.NewMessage=Nouveau message
+popupMenu.itemLabel.ShowHide=Montrer/Cacher
 tooltip.unread_messages=#1 mesage non lu;#1 messages non lus
 tooltip.new_messages=Nouveaux messages !
diff --git a/src/chrome/locale/hr-HR/overlay.properties b/src/chrome/locale/hr-HR/overlay.properties
index a4f4c0c..b3f4e8c 100644
--- a/src/chrome/locale/hr-HR/overlay.properties
+++ b/src/chrome/locale/hr-HR/overlay.properties
@@ -4,5 +4,6 @@ popupMenu.itemLabel.Preferences=Osobitosti
 popupMenu.itemLabel.Quit=Zatvori
 popupMenu.itemLabel.NewWindow=Novi prozor
 popupMenu.itemLabel.NewMessage=Nova poruka
+popupMenu.itemLabel.ShowHide=Show/Hide
 tooltip.unread_messages=#1 nepročitana poruka;#1 nepročitano poruka
 tooltip.new_messages=Nove poruke!
diff --git a/src/chrome/locale/it/overlay.properties b/src/chrome/locale/it/overlay.properties
index f5199d0..f3c4013 100644
--- a/src/chrome/locale/it/overlay.properties
+++ b/src/chrome/locale/it/overlay.properties
@@ -4,5 +4,6 @@ popupMenu.itemLabel.Preferences=Preferenze
 popupMenu.itemLabel.Quit=Esci
 popupMenu.itemLabel.NewWindow=Nuova finestra
 popupMenu.itemLabel.NewMessage=Nuovo messaggio
+popupMenu.itemLabel.ShowHide=Show/Hide
 tooltip.unread_messages=#1 messaggio non letto;#1 messaggi non letti
 tooltip.new_messages=Nuovi messaggi
diff --git a/src/chrome/locale/nl/overlay.properties b/src/chrome/locale/nl/overlay.properties
index 49b88de..df22f86 100644
--- a/src/chrome/locale/nl/overlay.properties
+++ b/src/chrome/locale/nl/overlay.properties
@@ -4,5 +4,6 @@ popupMenu.itemLabel.Preferences=Preferences
 popupMenu.itemLabel.Quit=Stoppen
 popupMenu.itemLabel.NewWindow=Nieuw venster
 popupMenu.itemLabel.NewMessage=Nieuw bericht
+popupMenu.itemLabel.ShowHide=Show/Hide
 tooltip.unread_messages=#1 ongelezen bericht;#1 ongelezen berichten
 tooltip.new_messages=Nieuwe berichten!
diff --git a/src/chrome/locale/pl-PL/overlay.properties b/src/chrome/locale/pl-PL/overlay.properties
index c32043d..215e3d1 100644
--- a/src/chrome/locale/pl-PL/overlay.properties
+++ b/src/chrome/locale/pl-PL/overlay.properties
@@ -4,5 +4,6 @@ popupMenu.itemLabel.Preferences=Ustawienia
 popupMenu.itemLabel.Quit=Wyjdź
 popupMenu.itemLabel.NewWindow=Nowe okno
 popupMenu.itemLabel.NewMessage=Nowa wiadomość
+popupMenu.itemLabel.ShowHide=Show/Hide
 tooltip.unread_messages=#1 nieprzeczytana wiadomość;#1 nieprzeczytane wiadomości
 tooltip.new_messages=Nowe wiadomości !
diff --git a/src/chrome/locale/ru-RU/overlay.properties b/src/chrome/locale/ru-RU/overlay.properties
index b641790..899f3cd 100644
--- a/src/chrome/locale/ru-RU/overlay.properties
+++ b/src/chrome/locale/ru-RU/overlay.properties
@@ -4,5 +4,6 @@ popupMenu.itemLabel.Preferences=Настройки
 popupMenu.itemLabel.Quit=Выход
 popupMenu.itemLabel.NewWindow=Новое окно
 popupMenu.itemLabel.NewMessage=Создать сообщение
+popupMenu.itemLabel.ShowHide=Show/Hide
 tooltip.unread_messages=#1 непрочитанных сообщений;#1 непрочитанных сообщений
 tooltip.new_messages=Новые сообщения !
diff --git a/src/chrome/locale/sk-SK/overlay.properties b/src/chrome/locale/sk-SK/overlay.properties
index 703a3dc..bbab61c 100644
--- a/src/chrome/locale/sk-SK/overlay.properties
+++ b/src/chrome/locale/sk-SK/overlay.properties
@@ -4,5 +4,6 @@ popupMenu.itemLabel.Preferences=Preferences
 popupMenu.itemLabel.Quit=Koniec
 popupMenu.itemLabel.NewWindow=Nové okno
 popupMenu.itemLabel.NewMessage=Nová správa
+popupMenu.itemLabel.ShowHide=Show/Hide
 tooltip.unread_messages=#1 neprečítaná správa;#1 neprečítané správy;#1 neprečítaných správ
 tooltip.new_messages=Nové správy!
diff --git a/src/chrome/locale/uk-UA/overlay.properties b/src/chrome/locale/uk-UA/overlay.properties
index 022fe86..c814946 100644
--- a/src/chrome/locale/uk-UA/overlay.properties
+++ b/src/chrome/locale/uk-UA/overlay.properties
@@ -4,5 +4,6 @@ popupMenu.itemLabel.Preferences=Вподобання
 popupMenu.itemLabel.Quit=Вийти
 popupMenu.itemLabel.NewWindow=Нове вікно
 popupMenu.itemLabel.NewMessage=Нове повідомлення
+popupMenu.itemLabel.ShowHide=Show/Hide
 tooltip.unread_messages=#1 непрочитане повідомлення;#1 непрочитані повідомлення
 tooltip.new_messages=Нові повідомлення!
diff --git a/src/chrome/locale/zh-TW/overlay.properties b/src/chrome/locale/zh-TW/overlay.properties
index 52ebdcc..75f1758 100644
--- a/src/chrome/locale/zh-TW/overlay.properties
+++ b/src/chrome/locale/zh-TW/overlay.properties
@@ -4,5 +4,6 @@ popupMenu.itemLabel.Preferences=Preferences
 popupMenu.itemLabel.Quit=結束
 popupMenu.itemLabel.NewWindow=開新視窗
 popupMenu.itemLabel.NewMessage=新增訊息
+popupMenu.itemLabel.ShowHide=Show/Hide
 tooltip.unread_messages=#1 封未讀訊息;#1 封未讀訊息
 tooltip.new_messages=有新訊息!
diff --git a/src/modules/FiretrayHandler.jsm b/src/modules/FiretrayHandler.jsm
index 223ea66..3b7e55c 100644
--- a/src/modules/FiretrayHandler.jsm
+++ b/src/modules/FiretrayHandler.jsm
@@ -85,7 +85,7 @@ firetray.Handler = {
 
     this.support['chat'] = FIRETRAY_CHAT_SUPPORTED_OS
       .indexOf(this.runtimeOS) > -1;
-    this.support['full_feat']  = FIRETRAY_FULL_FEAT_SUPPORTED_OS
+    this.support['full_feat'] = FIRETRAY_FULL_FEAT_SUPPORTED_OS
       .indexOf(firetray.Handler.runtimeOS) > -1;
 
     if (this.appId === FIRETRAY_APP_DB['thunderbird']['id'] ||
@@ -328,6 +328,7 @@ firetray.Handler = {
   },
 
   // these get overridden in OS-specific Icon/Window handlers
+  loadIcons: function() {},
   setIconImageDefault: function() {},
   setIconImageNewMail: function() {},
   setIconImageCustom: function(prefname) {},
@@ -551,7 +552,7 @@ firetray.PrefListener = new PrefListener(
       firetray.Messaging.updateIcon();
       break;
     case 'new_mail_icon_names':
-      firetray.StatusIcon.loadThemedIcons();
+      this.loadIcons();
     case 'excluded_folders_flags':
     case 'folder_count_recursive':
     case 'mail_accounts':
@@ -562,7 +563,7 @@ firetray.PrefListener = new PrefListener(
     case 'app_mail_icon_names':
     case 'app_browser_icon_names':
     case 'app_default_icon_names':
-      firetray.StatusIcon.loadThemedIcons(); // linux
+      this.loadIcons(); // linux
     case 'app_icon_custom':
     case 'mail_icon_custom':
       firetray.StatusIcon.loadImageCustom(name);
diff --git a/src/modules/ctypes/linux/appindicator.jsm b/src/modules/ctypes/linux/appindicator.jsm
index 0a39ce4..ed7a10e 100644
--- a/src/modules/ctypes/linux/appindicator.jsm
+++ b/src/modules/ctypes/linux/appindicator.jsm
@@ -1,4 +1,4 @@
-/* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 
 var EXPORTED_SYMBOLS = [ "appind3" ];
 
@@ -43,9 +43,16 @@ function appindicator_defines(lib) {
   lib.lazy_bind("app_indicator_set_status", ctypes.void_t, this.AppIndicator.ptr, this.AppIndicatorStatus);
   lib.lazy_bind("app_indicator_get_status", this.AppIndicatorStatus, this.AppIndicator.ptr);
   lib.lazy_bind("app_indicator_set_menu", ctypes.void_t, this.AppIndicator.ptr, gtk.GtkMenu.ptr);
+  lib.lazy_bind("app_indicator_set_icon", ctypes.void_t, this.AppIndicator.ptr, gobject.gchar.ptr);
+  lib.lazy_bind("app_indicator_set_attention_icon", ctypes.void_t, this.AppIndicator.ptr, gobject.gchar.ptr);
+  lib.lazy_bind("app_indicator_set_label", ctypes.void_t, this.AppIndicator.ptr, gobject.gchar.ptr, gobject.gchar.ptr);
+  lib.lazy_bind("app_indicator_set_secondary_activate_target", ctypes.void_t, this.AppIndicator.ptr, gtk.GtkWidget.ptr);
 
-  this.AppIndicatorConnectionChangedCb_t = ctypes.FunctionType(
+  this.ConnectionChangedCb_t = ctypes.FunctionType(
     ctypes.default_abi, ctypes.void_t, [this.AppIndicator.ptr, gobject.gboolean, gobject.gpointer]).ptr;
+
+  this.OnScrollCb_t = ctypes.FunctionType(
+    ctypes.default_abi, ctypes.void_t, [this.AppIndicator.ptr, gobject.gint, gobject.guint, gobject.gpointer]).ptr;
 };
 
-appind3 = new ctypes_library(APPINDICATOR_LIBNAME, APPINDICATOR_ABIS, appindicator_defines, this);
+var appind3 = new ctypes_library(APPINDICATOR_LIBNAME, APPINDICATOR_ABIS, appindicator_defines, this);
diff --git a/src/modules/ctypes/linux/gtk.jsm b/src/modules/ctypes/linux/gtk.jsm
index 95c84b6..267aabb 100644
--- a/src/modules/ctypes/linux/gtk.jsm
+++ b/src/modules/ctypes/linux/gtk.jsm
@@ -123,6 +123,7 @@ function gtk_defines(lib) {
   lib.lazy_bind("gtk_image_menu_item_set_image", ctypes.void_t, this.GtkImageMenuItem.ptr, this.GtkWidget.ptr);
   lib.lazy_bind("gtk_menu_shell_append", ctypes.void_t, this.GtkMenuShell.ptr, this.GtkWidget.ptr);
   lib.lazy_bind("gtk_menu_shell_prepend", ctypes.void_t, this.GtkMenuShell.ptr, this.GtkWidget.ptr);
+  lib.lazy_bind("gtk_menu_shell_insert", ctypes.void_t, this.GtkMenuShell.ptr, this.GtkWidget.ptr, gobject.gint);
   lib.lazy_bind("gtk_menu_popup", ctypes.void_t, this.GtkMenu.ptr, this.GtkWidget.ptr, this.GtkWidget.ptr, this.GtkMenuPositionFunc_t, gobject.gpointer, gobject.guint, gobject.guint);
   lib.lazy_bind("gtk_status_icon_position_menu", ctypes.void_t, this.GtkMenu.ptr, gobject.gint.ptr, gobject.gint.ptr, gobject.gboolean.ptr, gobject.gpointer);
   lib.lazy_bind("gtk_separator_menu_item_new", this.GtkWidget.ptr);
@@ -155,6 +156,7 @@ function gtk_defines(lib) {
   lib.lazy_bind("gtk_widget_get_events", gobject.gint, this.GtkWidget.ptr);
   lib.lazy_bind("gtk_widget_add_events", ctypes.void_t, this.GtkWidget.ptr, gobject.gint);
   lib.lazy_bind("gtk_widget_get_toplevel", this.GtkWidget.ptr, this.GtkWidget.ptr);
+  lib.lazy_bind("gtk_widget_set_sensitive", ctypes.void_t, this.GtkWidget.ptr, gobject.gboolean);
   lib.lazy_bind("gtk_window_get_type", gobject.GType);
   lib.lazy_bind("gtk_window_get_position", ctypes.void_t, this.GtkWindow.ptr, gobject.gint.ptr, gobject.gint.ptr);
   lib.lazy_bind("gtk_window_move", ctypes.void_t, this.GtkWindow.ptr, gobject.gint, gobject.gint);
diff --git a/src/modules/linux/FiretrayAppIndicator.jsm b/src/modules/linux/FiretrayAppIndicator.jsm
new file mode 100644
index 0000000..a48ce23
--- /dev/null
+++ b/src/modules/linux/FiretrayAppIndicator.jsm
@@ -0,0 +1,136 @@
+/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+var EXPORTED_SYMBOLS = [ "firetray" ];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/ctypes.jsm");
+// FIXME: can't subscribeLibsForClosing([appind3])
+// https://bugs.launchpad.net/ubuntu/+source/firefox/+bug/1393256
+Cu.import("resource://firetray/ctypes/linux/appindicator.jsm");
+Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
+Cu.import("resource://firetray/ctypes/linux/gtk.jsm");
+Cu.import("resource://firetray/commons.js");
+firetray.Handler.subscribeLibsForClosing([gobject, gtk]);
+
+let log = firetray.Logging.getLogger("firetray.AppIndicator");
+
+if ("undefined" == typeof(firetray.StatusIcon))
+  log.error("This module MUST be imported from/after FiretrayStatusIcon !");
+
+
+firetray.AppIndicator = {
+  initialized: false,
+  callbacks: {},
+  indicator: null,
+
+  init: function() {
+    this.indicator = appind3.app_indicator_new(
+      FIRETRAY_APPINDICATOR_ID,
+      firetray.StatusIcon.defaultAppIconName,
+      appind3.APP_INDICATOR_CATEGORY_COMMUNICATIONS
+    );
+    appind3.app_indicator_set_status(this.indicator,
+                                     appind3.APP_INDICATOR_STATUS_ACTIVE);
+    appind3.app_indicator_set_menu(this.indicator,
+                                   firetray.PopupMenu.menu); // mandatory
+    log.debug("indicator="+this.indicator);
+
+    this.addCallbacks();
+
+    for (let item in firetray.PopupMenu.menuShowHide) {
+      firetray.PopupMenu.showItem(firetray.PopupMenu.menuShowHide[item]);
+    }
+    let menuItemShowHideWidget = ctypes.cast(
+      firetray.PopupMenu.menuShowHide.item, gtk.GtkWidget.ptr);
+    // FIXME: XXX should be option ShowHide or ActivateLast
+    appind3.app_indicator_set_secondary_activate_target(
+      this.indicator, menuItemShowHideWidget);
+    firetray.Handler.setIconTooltipDefault();
+
+    this.initialized = true;
+    return true;
+  },
+
+  shutdown: function() {
+    log.debug("Disabling AppIndicator");
+    gobject.g_object_unref(this.indicator);
+    this.initialized = false;
+  },
+
+  addCallbacks: function() {
+    this.callbacks.connChanged = appind3.ConnectionChangedCb_t(
+      firetray.AppIndicator.onConnectionChanged); // void return, no sentinel
+    gobject.g_signal_connect(this.indicator, "connection-changed",
+                             firetray.AppIndicator.callbacks.connChanged, null);
+
+    this.callbacks.onScroll = appind3.OnScrollCb_t(
+      firetray.AppIndicator.onScroll); // void return, no sentinel
+    gobject.g_signal_connect(this.indicator, "scroll-event",
+                             firetray.AppIndicator.callbacks.onScroll, null);
+  },
+
+  onConnectionChanged: function(indicator, connected, data) {
+    log.debug("AppIndicator connection-changed: "+connected);
+  },
+
+  // https://bugs.kde.org/show_bug.cgi?id=340978 broken under KDE4
+  onScroll: function(indicator, delta, direction, data) { // AppIndicator*, gint, GdkScrollDirection, gpointer
+    log.debug("onScroll: "+direction);
+    firetray.StatusIcon.onScroll(direction);
+  },
+
+};  // AppIndicator
+
+firetray.StatusIcon.initImpl =
+  firetray.AppIndicator.init.bind(firetray.AppIndicator);
+
+firetray.StatusIcon.shutdownImpl =
+  firetray.AppIndicator.shutdown.bind(firetray.AppIndicator);
+
+firetray.Handler.setIconImageDefault = function() {
+  log.debug("setIconImageDefault");
+  appind3.app_indicator_set_icon(firetray.AppIndicator.indicator,
+                                 firetray.StatusIcon.defaultAppIconName);
+};
+
+firetray.Handler.setIconImageNewMail = function() {
+  log.debug("setIconImageNewMail");
+  appind3.app_indicator_set_icon(firetray.AppIndicator.indicator,
+                                 firetray.StatusIcon.defaultNewMailIconName);
+};
+
+firetray.Handler.setIconImageCustom = function(prefname) {
+  let prefCustomIconPath = firetray.Utils.prefService.getCharPref(prefname);
+  // Undocumented: ok to pass a *path* instead of an icon name! Otherwise we
+  // should be changing the default icons (which is maybe a better
+  // implementation anyway)...
+  appind3.app_indicator_set_icon(firetray.AppIndicator.indicator, prefCustomIconPath);
+};
+
+// No tooltips in AppIndicator
+// https://bugs.launchpad.net/indicator-application/+bug/527458
+firetray.Handler.setIconTooltip = function(toolTipStr) {
+  log.debug("setIconTooltip");
+  if (!firetray.AppIndicator.indicator)
+    return false;
+  firetray.PopupMenu.setItemLabel(firetray.PopupMenu.menuShowHide.tip,
+                                  toolTipStr);
+  return true;
+};
+
+// AppIndicator doesn't support pixbuf https://bugs.launchpad.net/bugs/812067
+firetray.Handler.setIconText = function(text, color) { };
+
+firetray.Handler.setIconVisibility = function(visible) {
+  if (!firetray.AppIndicator.indicator)
+    return false;
+
+  let status = visible ?
+        appind3.APP_INDICATOR_STATUS_ACTIVE :
+        appind3.APP_INDICATOR_STATUS_PASSIVE;
+  appind3.app_indicator_set_status(firetray.AppIndicator.indicator, status);
+  return true;
+};
diff --git a/src/modules/linux/FiretrayChatStatusIcon.jsm b/src/modules/linux/FiretrayChatStatusIcon.jsm
index 0e98ab4..17646d4 100644
--- a/src/modules/linux/FiretrayChatStatusIcon.jsm
+++ b/src/modules/linux/FiretrayChatStatusIcon.jsm
@@ -10,13 +10,13 @@ Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/ctypes.jsm");
 Cu.import("resource://firetray/ctypes/ctypesMap.jsm");
-Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
-Cu.import("resource://firetray/ctypes/linux/gio.jsm");
 Cu.import("resource://firetray/ctypes/linux/gdk.jsm");
+Cu.import("resource://firetray/ctypes/linux/gio.jsm");
+Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
 Cu.import("resource://firetray/ctypes/linux/gtk.jsm");
 Cu.import("resource://firetray/linux/FiretrayWindow.jsm");
 Cu.import("resource://firetray/commons.js");
-firetray.Handler.subscribeLibsForClosing([gobject, gio, gtk]);
+firetray.Handler.subscribeLibsForClosing([gdk, gio, gobject, gtk]);
 
 if ("undefined" == typeof(firetray.Handler))
   log.error("This module MUST be imported from/after FiretrayHandler !");
@@ -51,7 +51,7 @@ firetray.ChatStatusIcon = {
 
   init: function() {
     if (!firetray.Handler.appHasChat) throw "ChatStatusIcon for chat app only";
-    if (!firetray.GtkIcons.initialized) throw "GtkIcons should have been initialized by StatusIcon";
+    firetray.GtkIcons.init();
 
     this.trayIcon = gtk.gtk_status_icon_new();
     this.loadThemedIcons();
diff --git a/src/modules/linux/FiretrayGtkIcons.jsm b/src/modules/linux/FiretrayGtkIcons.jsm
index 33fdec8..ecdd438 100644
--- a/src/modules/linux/FiretrayGtkIcons.jsm
+++ b/src/modules/linux/FiretrayGtkIcons.jsm
@@ -25,7 +25,7 @@ firetray.GtkIcons = {
     try {
       if (this.initialized) return true;
 
-      this.loadDefaultTheme();
+      this.appendSearchPath();
       this.initialized = true;
       return true;
     } catch (x) {
@@ -35,10 +35,11 @@ firetray.GtkIcons = {
   },
 
   shutdown: function() {
+    // FIXME: XXX destroy icon here
     this.initialized = false;
   },
 
-  loadDefaultTheme: function() {
+  appendSearchPath: function() {
     this.GTK_THEME_ICON_PATH = firetray.Utils.chromeToPath("chrome://firetray/skin/icons/linux");
     log.debug(this.GTK_THEME_ICON_PATH);
     let gtkIconTheme = gtk.gtk_icon_theme_get_default();
diff --git a/src/modules/linux/FiretrayStatusIcon.jsm b/src/modules/linux/FiretrayGtkStatusIcon.jsm
similarity index 54%
copy from src/modules/linux/FiretrayStatusIcon.jsm
copy to src/modules/linux/FiretrayGtkStatusIcon.jsm
index 04a9afb..2182f2d 100644
--- a/src/modules/linux/FiretrayStatusIcon.jsm
+++ b/src/modules/linux/FiretrayGtkStatusIcon.jsm
@@ -6,145 +6,70 @@ const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/ctypes.jsm");
 Cu.import("resource://firetray/ctypes/linux/cairo.jsm");
-Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
 Cu.import("resource://firetray/ctypes/linux/gdk.jsm");
 Cu.import("resource://firetray/ctypes/linux/gio.jsm");
-Cu.import("resource://firetray/ctypes/linux/glib.jsm");
+Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
 Cu.import("resource://firetray/ctypes/linux/gtk.jsm");
 Cu.import("resource://firetray/ctypes/linux/pango.jsm");
 Cu.import("resource://firetray/ctypes/linux/pangocairo.jsm");
+Cu.import("resource://firetray/linux/FiretrayGtkIcons.jsm");
 Cu.import("resource://firetray/commons.js");
-firetray.Handler.subscribeLibsForClosing([cairo, gobject, gdk, gio, gtk, pango, pangocairo]);
+firetray.Handler.subscribeLibsForClosing([cairo, gdk, gio, gobject, gtk, pango,
+  pangocairo]);
 
-let log = firetray.Logging.getLogger("firetray.StatusIcon");
+let log = firetray.Logging.getLogger("firetray.GtkStatusIcon");
 
 if ("undefined" == typeof(firetray.Handler))
-  log.error("This module MUST be imported from/after FiretrayHandler !");
+  log.error("This module MUST be imported from/after FiretrayStatusIcon !");
 
 
-firetray.StatusIcon = {
+firetray.GtkStatusIcon = {
   MIN_FONT_SIZE: 4,
   FILENAME_BLANK: null,
+  GTK_THEME_ICON_PATH: null,
 
   initialized: false,
-  callbacks: {}, // pointers to JS functions. MUST LIVE DURING ALL THE EXECUTION
+  callbacks: {},
   trayIcon: null,
   themedIconApp: null,
   themedIconNewMail: null,
-  prefAppIconNames: null,
-  prefNewMailIconNames: null,
-  defaultAppIconName: null,
-  defaultNewMailIconName: null,
-  inidicator: null,
 
   init: function() {
     this.FILENAME_BLANK = firetray.Utils.chromeToPath(
       "chrome://firetray/skin/icons/blank-icon.png");
 
-    Cu.import("resource://firetray/linux/FiretrayGtkIcons.jsm");
     firetray.GtkIcons.init();
-    this.defineIconNames();
     this.loadThemedIcons();
 
-    Cu.import("resource://firetray/linux/FiretrayPopupMenu.jsm");
-    if (!firetray.PopupMenu.init())
-      return false;
-
-    // FIXME: we may want to split into 2 separate modules: GtkStatusIcon and
-    // AppIndicator.
-    Cu.import("resource://firetray/ctypes/linux/appindicator.jsm");
-    if (appind3.available() && this.dbusNotificationWatcherReady()) {
-      firetray.Handler.subscribeLibsForClosing([appind3]);
-      this.indicatorInit();
-    } else {
-      this.trayIcon = gtk.gtk_status_icon_new();
-      firetray.Handler.setIconImageDefault();
-      firetray.Handler.setIconTooltipDefault();
-      this.addCallbacks();
-    }
+    this.trayIcon = gtk.gtk_status_icon_new();
+    firetray.Handler.setIconImageDefault();
+    firetray.Handler.setIconTooltipDefault();
+    this.addCallbacks();
 
     this.initialized = true;
     return true;
   },
 
   shutdown: function() {
-    log.debug("Disabling StatusIcon");
-    firetray.PopupMenu.shutdown();
-    // FIXME: should destroy/hide icon here
+    log.debug("Disabling GtkStatusIcon");
     firetray.GtkIcons.shutdown();
+    // FIXME: XXX destroy icon here
     this.initialized = false;
   },
 
-  indicatorInit: function() {
-    this.indicator = appind3.app_indicator_new(
-      FIRETRAY_APPINDICATOR_ID,
-      'firefox',
-      appind3.APP_INDICATOR_CATEGORY_COMMUNICATIONS
-    );
-    appind3.app_indicator_set_status(this.indicator, appind3.APP_INDICATOR_STATUS_ACTIVE);
-    appind3.app_indicator_set_menu(this.indicator, firetray.PopupMenu.menu); // mandatory
-    log.warn("indicator="+this.indicator);
-    /*
-     let gval = new gobject.gboolean;
-     gobject.g_object_get(
-     ctypes.cast(this.indicator, gobject.gpointer),
-     "connected",
-     gval.address(),
-     ctypes.voidptr_t(null)
-     );
-     log.warn("gval="+gval+" true? "+!firetray.js.strEquals(gval, gobject.FALSE));
-     */
-    this.callbacks.indicator = appind3.AppIndicatorConnectionChangedCb_t(
-      firetray.StatusIcon.onAppIndicatorConnectionChanged); // void return, no sentinel
-    gobject.g_signal_connect(this.indicator, "connection-changed",
-                             firetray.StatusIcon.callbacks.indicator, null);
-    log.warn("status="+appind3.app_indicator_get_status(this.indicator));
-  },
-
-  defineIconNames: function() {
-    this.prefAppIconNames = (function() {
-      if (firetray.Handler.inMailApp) {
-        return "app_mail_icon_names";
-      } else if (firetray.Handler.inBrowserApp) {
-        return "app_browser_icon_names";
-      } else {
-        return "app_default_icon_names";
-      }
-    })();
-    this.defaultAppIconName = firetray.Handler.appName.toLowerCase();
-
-    this.prefNewMailIconNames = "new_mail_icon_names";
-    this.defaultNewMailIconName = "mail-unread";
-  },
-
   loadThemedIcons: function() {
     if (firetray.Handler.inMailApp) {
-      let newMailIconNames = this.getNewMailIconNames();
+      let newMailIconNames = firetray.StatusIcon.getNewMailIconNames();
       if (this.themedIconNewMail) gobject.g_object_unref(this.themedIconNewMail);
       this.themedIconNewMail = this.initThemedIcon(newMailIconNames);
     }
-    let appIconNames = this.getAppIconNames();
+    let appIconNames = firetray.StatusIcon.getAppIconNames();
     if (this.themedIconApp) gobject.g_object_unref(this.themedIconApp);
     this.themedIconApp = this.initThemedIcon(appIconNames);
   },
 
-  loadImageCustom: function() { }, // done in setIconImageCustom
-
-  getAppIconNames: function() {
-    let appIconNames = firetray.Utils.getArrayPref(this.prefAppIconNames);
-    appIconNames.push(this.defaultAppIconName);
-    return appIconNames;
-  },
-  getNewMailIconNames: function() {
-    let newMailIconNames = firetray.Utils.getArrayPref(this.prefNewMailIconNames);
-    newMailIconNames.push(this.defaultNewMailIconName);
-    return newMailIconNames;
-  },
-
   initThemedIcon: function(names) {
     if (!firetray.js.isArray(names)) throw new TypeError();
     log.debug("themedIconNames="+names);
@@ -160,58 +85,37 @@ firetray.StatusIcon = {
   },
 
   addCallbacks: function() {
+    Cu.import("resource://firetray/linux/FiretrayPopupMenu.jsm");
     /* NOTE: here we do use a function handler (instead of a function
      definition) because we need the args passed to it ! As a consequence, we
      need to abandon 'this' in PopupMenu.popup() */
     this.callbacks.menuPopup = gtk.GCallbackMenuPopup_t(firetray.PopupMenu.popup); // void return, no sentinel
     gobject.g_signal_connect(this.trayIcon, "popup-menu",
-      firetray.StatusIcon.callbacks.menuPopup, firetray.PopupMenu.menu);
+      firetray.GtkStatusIcon.callbacks.menuPopup, firetray.PopupMenu.menu);
     this.callbacks.onScroll = gtk.GCallbackOnScroll_t(
-      firetray.StatusIcon.onScroll, null, FIRETRAY_CB_SENTINEL);
+      firetray.GtkStatusIcon.onScroll, null, FIRETRAY_CB_SENTINEL);
     gobject.g_signal_connect(this.trayIcon, "scroll-event",
-      firetray.StatusIcon.callbacks.onScroll, null);
+      firetray.GtkStatusIcon.callbacks.onScroll, null);
 
     log.debug("showHideAllWindows: "+firetray.Handler.hasOwnProperty("showHideAllWindows"));
     this.callbacks.iconActivate = gtk.GCallbackStatusIconActivate_t(
-      firetray.StatusIcon.onClick, null, FIRETRAY_CB_SENTINEL);
-    let handlerId = gobject.g_signal_connect(firetray.StatusIcon.trayIcon,
-      "activate", firetray.StatusIcon.callbacks.iconActivate, null);
+      firetray.GtkStatusIcon.onClick, null, FIRETRAY_CB_SENTINEL);
+    let handlerId = gobject.g_signal_connect(firetray.GtkStatusIcon.trayIcon,
+      "activate", firetray.GtkStatusIcon.callbacks.iconActivate, null);
     log.debug("g_connect activate="+handlerId);
 
     this.callbacks.iconMiddleClick = gtk.GCallbackStatusIconMiddleClick_t(
       firetray.Handler.activateLastWindowCb, null, FIRETRAY_CB_SENTINEL);
-    handlerId = gobject.g_signal_connect(firetray.StatusIcon.trayIcon,
-      "button-press-event", firetray.StatusIcon.callbacks.iconMiddleClick, null);
+    handlerId = gobject.g_signal_connect(firetray.GtkStatusIcon.trayIcon,
+      "button-press-event", firetray.GtkStatusIcon.callbacks.iconMiddleClick, null);
     log.debug("g_connect middleClick="+handlerId);
   },
 
   onScroll: function(icon, event, data) {
-    if (!firetray.Utils.prefService.getBoolPref("scroll_hides"))
-      return false;
-
-    let iconGpointer = ctypes.cast(icon, gobject.gpointer);
     let gdkEventScroll = ctypes.cast(event, gdk.GdkEventScroll.ptr);
-    let scroll_mode = firetray.Utils.prefService.getCharPref("scroll_mode");
-
     let direction = gdkEventScroll.contents.direction;
-    switch(direction) {
-    case gdk.GDK_SCROLL_UP:
-      log.debug("SCROLL UP");
-      if (scroll_mode === "down_hides")
-        firetray.Handler.showAllWindows();
-      else if (scroll_mode === "up_hides")
-        firetray.Handler.hideAllWindows();
-      break;
-    case gdk.GDK_SCROLL_DOWN:
-      log.debug("SCROLL DOWN");
-      if (scroll_mode === "down_hides")
-        firetray.Handler.hideAllWindows();
-      else if (scroll_mode === "up_hides")
-        firetray.Handler.showAllWindows();
-      break;
-    default:
-      log.error("SCROLL UNKNOWN");
-    }
+
+    firetray.StatusIcon.onScroll(direction);
 
     let stopPropagation = false;
     return stopPropagation;
@@ -224,98 +128,61 @@ firetray.StatusIcon = {
   },
 
   setIconImageFromFile: function(filename) {
-    if (!firetray.StatusIcon.trayIcon)
+    if (!firetray.GtkStatusIcon.trayIcon)
       log.error("Icon missing");
     log.debug(filename);
-    gtk.gtk_status_icon_set_from_file(firetray.StatusIcon.trayIcon,
+    gtk.gtk_status_icon_set_from_file(firetray.GtkStatusIcon.trayIcon,
                                       filename);
   },
 
   setIconImageFromGIcon: function(gicon) {
-    if (!firetray.StatusIcon.trayIcon || !gicon)
+    if (!firetray.GtkStatusIcon.trayIcon || !gicon)
       log.error("Icon missing");
     log.debug(gicon);
-    gtk.gtk_status_icon_set_from_gicon(firetray.StatusIcon.trayIcon, gicon);
+    gtk.gtk_status_icon_set_from_gicon(firetray.GtkStatusIcon.trayIcon, gicon);
   },
 
-  onAppIndicatorConnectionChanged: function(indicator, connected, data) {
-    log.warn("AppIndicator connection-changed: "+connected);
-  },
+};                              // GtkStatusIcon
 
-  dbusNotificationWatcherReady: function() {
-    let watcherReady = false;
+firetray.StatusIcon.initImpl =
+  firetray.GtkStatusIcon.init.bind(firetray.GtkStatusIcon);
 
-    function error(e) {
-      if (!e.isNull()) {
-        log.error(e.contents.message);
-        glib.g_error_free(e);
-      }
-    }
+firetray.StatusIcon.shutdownImpl =
+  firetray.GtkStatusIcon.shutdown.bind(firetray.GtkStatusIcon);
 
-    let conn = new gio.GDBusConnection.ptr;
-    let err = new glib.GError.ptr(null);
-    conn = gio.g_bus_get_sync(gio.G_BUS_TYPE_SESSION, null, err.address());
-    if (error(err)) return watcherReady;
-
-    if (!conn.isNull()) {
-      let flags = gio.G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START |
-            gio.G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
-            gio.G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS;
-
-      let proxy = gio.g_dbus_proxy_new_for_bus_sync(
-        gio.G_BUS_TYPE_SESSION,
-        flags,
-        null, /* GDBusInterfaceInfo */
-        appind3.NOTIFICATION_WATCHER_DBUS_ADDR,
-        appind3.NOTIFICATION_WATCHER_DBUS_OBJ,
-        appind3.NOTIFICATION_WATCHER_DBUS_IFACE,
-        null, /* GCancellable */
-        err.address());
-      if (error(err)) return watcherReady;
-
-      if (!proxy.isNull()) {
-        let owner = gio.g_dbus_proxy_get_name_owner(proxy);
-        if (!owner.isNull()) {
-          watcherReady = true;
-        }
-        gobject.g_object_unref(proxy);
-      }
-
-      gobject.g_object_unref(conn);
-    }
-
-    return watcherReady;
-  }
-
-}; // firetray.StatusIcon
+firetray.Handler.loadIcons = firetray.GtkStatusIcon.loadThemedIcons;
 
 firetray.Handler.setIconImageDefault = function() {
   log.debug("setIconImageDefault");
-  if (!firetray.StatusIcon.themedIconApp)
+  if (!firetray.GtkStatusIcon.themedIconApp)
     throw "Default application themed icon not set";
   let appIconType = firetray.Utils.prefService.getIntPref("app_icon_type");
-  if (appIconType === FIRETRAY_APPLICATION_ICON_TYPE_THEMED)
-    firetray.StatusIcon.setIconImageFromGIcon(firetray.StatusIcon.themedIconApp);
-  else if (appIconType === FIRETRAY_APPLICATION_ICON_TYPE_CUSTOM)
+  if (appIconType === FIRETRAY_APPLICATION_ICON_TYPE_THEMED) {
+    firetray.GtkStatusIcon.setIconImageFromGIcon(
+      firetray.GtkStatusIcon.themedIconApp);
+  } else if (appIconType === FIRETRAY_APPLICATION_ICON_TYPE_CUSTOM) {
     firetray.Handler.setIconImageCustom("app_icon_custom");
+  }
 };
 
 firetray.Handler.setIconImageNewMail = function() {
-  firetray.StatusIcon.setIconImageFromGIcon(firetray.StatusIcon.themedIconNewMail);
+  firetray.GtkStatusIcon.setIconImageFromGIcon(
+    firetray.GtkStatusIcon.themedIconNewMail);
 };
 
 firetray.Handler.setIconImageCustom = function(prefname) {
   let prefCustomIconPath = firetray.Utils.prefService.getCharPref(prefname);
-  firetray.StatusIcon.setIconImageFromFile(prefCustomIconPath);
+  firetray.GtkStatusIcon.setIconImageFromFile(prefCustomIconPath);
 };
 
 // GTK bug: Gdk-CRITICAL **: IA__gdk_window_get_root_coords: assertion `GDK_IS_WINDOW (window)' failed
 firetray.Handler.setIconTooltip = function(toolTipStr) {
-  if (!firetray.StatusIcon.trayIcon)
+  if (!firetray.GtkStatusIcon.trayIcon)
     return false;
 
+  log.debug("setIconTooltip, toolTipStr="+toolTipStr);
   try {
-    gtk.gtk_status_icon_set_tooltip_text(firetray.StatusIcon.trayIcon,
+    gtk.gtk_status_icon_set_tooltip_text(firetray.GtkStatusIcon.trayIcon,
                                          toolTipStr);
   } catch (x) {
     log.error(x);
@@ -324,12 +191,6 @@ firetray.Handler.setIconTooltip = function(toolTipStr) {
   return true;
 };
 
-firetray.Handler.setIconTooltipDefault = function() {
-  if (!this.appName)
-    throw "application name not initialized";
-  this.setIconTooltip(this.appName);
-};
-
 firetray.Handler.setIconText = function(text, color) { // FIXME: function too long
   log.debug("setIconText, color="+color);
   if (typeof(text) != "string")
@@ -338,7 +199,7 @@ firetray.Handler.setIconText = function(text, color) { // FIXME: function too lo
   try {
     // build background from image
     let specialIcon = gdk.gdk_pixbuf_new_from_file(
-      firetray.StatusIcon.FILENAME_BLANK, null); // GError **error);
+      firetray.GtkStatusIcon.FILENAME_BLANK, null); // GError **error);
     let dest = gdk.gdk_pixbuf_copy(specialIcon);
     let w = gdk.gdk_pixbuf_get_width(specialIcon);
     let h = gdk.gdk_pixbuf_get_height(specialIcon);
@@ -390,8 +251,8 @@ firetray.Handler.setIconText = function(text, color) { // FIXME: function too lo
     // fit text to the icon by decreasing font size
     while ( tw.value > (w - border) || th.value > (h - border) ) {
       sz = pango.pango_font_description_get_size(fnt);
-      if(sz < firetray.StatusIcon.MIN_FONT_SIZE) {
-        sz = firetray.StatusIcon.MIN_FONT_SIZE;
+      if(sz < firetray.GtkStatusIcon.MIN_FONT_SIZE) {
+        sz = firetray.GtkStatusIcon.MIN_FONT_SIZE;
         break;
       }
       sz -= pango.PANGO_SCALE;
@@ -429,7 +290,7 @@ firetray.Handler.setIconText = function(text, color) { // FIXME: function too lo
     gobject.g_object_unref(bufAlpha);
 
     log.debug("gtk_status_icon_set_from_pixbuf="+dest);
-    gtk.gtk_status_icon_set_from_pixbuf(firetray.StatusIcon.trayIcon, dest);
+    gtk.gtk_status_icon_set_from_pixbuf(firetray.GtkStatusIcon.trayIcon, dest);
   } catch (x) {
     log.error(x);
     return false;
@@ -439,8 +300,8 @@ firetray.Handler.setIconText = function(text, color) { // FIXME: function too lo
 };
 
 firetray.Handler.setIconVisibility = function(visible) {
-  if (!firetray.StatusIcon.trayIcon)
+  if (!firetray.GtkStatusIcon.trayIcon)
     return false;
-  gtk.gtk_status_icon_set_visible(firetray.StatusIcon.trayIcon, visible);
+  gtk.gtk_status_icon_set_visible(firetray.GtkStatusIcon.trayIcon, visible);
   return true;
 };
diff --git a/src/modules/linux/FiretrayPopupMenu.jsm b/src/modules/linux/FiretrayPopupMenu.jsm
index eace46a..40957b0 100644
--- a/src/modules/linux/FiretrayPopupMenu.jsm
+++ b/src/modules/linux/FiretrayPopupMenu.jsm
@@ -21,11 +21,14 @@ if ("undefined" == typeof(firetray.StatusIcon))
 
 
 firetray.PopupMenu = {
+  MENU_ITEM_WINDOWS_POSITION: 3,
+
   initialized: false,
   callbacks: {menuItemWindowActivate: {}}, // FIXME: try to store them into a ctypes array/struct.
   menu: null,
   menuShell: null,
   menuSeparatorWindows: null,
+  menuShowHide: {tip: null, sep: null, item: null},
 
   init: function() {
     this.menu = gtk.gtk_menu_new();
@@ -33,16 +36,16 @@ firetray.PopupMenu = {
     var addMenuSeparator = false;
 
     if (firetray.Handler.inMailApp) {
-      this.addItem("ResetIcon", "gtk-apply", "activate",
-                   firetray.Handler.setIconImageDefault);
-      this.addItem("NewMessage", "gtk-edit", "activate",
-                   firetray.Handler.openMailMessage);
+      this.addItem({itemName:"ResetIcon", iconName:"gtk-apply",
+                    action:"activate", callback: firetray.Handler.setIconImageDefault});
+      this.addItem({itemName:"NewMessage", iconName:"gtk-edit",
+                    action:"activate", callback: firetray.Handler.openMailMessage});
       addMenuSeparator = true;
     }
 
     if (firetray.Handler.inBrowserApp) {
-      this.addItem("NewWindow", "gtk-new", "activate",
-                   firetray.Handler.openBrowserWindow);
+      this.addItem({itemName:"NewWindow", iconName:"gtk-new",
+                    action:"activate", callback: firetray.Handler.openBrowserWindow});
       addMenuSeparator = true;
     }
 
@@ -53,22 +56,25 @@ firetray.PopupMenu = {
                                                             gtk.GtkWidget.ptr));
     }
 
-    this.addItem("Preferences", "gtk-preferences", "activate",
-                 firetray.Handler.openPrefWindow);
+    this.addItem({itemName:"Preferences", iconName:"gtk-preferences",
+                  action:"activate", callback: firetray.Handler.openPrefWindow});
     menuSeparator = gtk.gtk_separator_menu_item_new();
     gtk.gtk_menu_shell_append(this.menuShell, ctypes.cast(menuSeparator,
                                                           gtk.GtkWidget.ptr));
 
-    this.addItem("Quit", "gtk-quit", "activate",
-                 firetray.Handler.quitApplication);
+    this.addItem({itemName:"Quit", iconName:"gtk-quit",
+                  action:"activate", callback: firetray.Handler.quitApplication});
 
     var menuWidget = ctypes.cast(this.menu, gtk.GtkWidget.ptr);
     gtk.gtk_widget_show_all(menuWidget);
 
-    var menuSeparatorWindows = gtk.gtk_separator_menu_item_new();
-    gtk.gtk_menu_shell_prepend(this.menuShell, ctypes.cast(menuSeparatorWindows,
-                                                           gtk.GtkWidget.ptr));
-    this.menuSeparatorWindows = menuSeparatorWindows;
+    // for hidden windows, not shown otherwise
+    this.menuSeparatorWindows = gtk.gtk_separator_menu_item_new();
+    gtk.gtk_menu_shell_prepend(
+      this.menuShell, ctypes.cast(this.menuSeparatorWindows, gtk.GtkWidget.ptr));
+
+    // for AppIndicator, not shown otherwise
+    this.prependAppIndicatorItems();
 
     this.initialized = true;
     return true;
@@ -79,22 +85,43 @@ firetray.PopupMenu = {
     this.initialized = false;
   },
 
-  addItem: function(itemName, iconName, action, callback) {
-    var menuItemLabel = firetray.Utils.strings.GetStringFromName("popupMenu.itemLabel."+itemName); // shouldn't need to convert to utf8 later thank to js-ctypes
+  addItem: function(it) {
+    var menuItemLabel = firetray.Utils.strings.GetStringFromName("popupMenu.itemLabel."+it.itemName); // shouldn't need to convert to utf8 later thank to js-ctypes
     var menuItem = gtk.gtk_image_menu_item_new_with_label(menuItemLabel);
-    var menuItemIcon = gtk.gtk_image_new_from_stock(iconName, gtk.GTK_ICON_SIZE_MENU);
+    var menuItemIcon = gtk.gtk_image_new_from_stock(it.iconName, gtk.GTK_ICON_SIZE_MENU);
     gtk.gtk_image_menu_item_set_image(menuItem, menuItemIcon);
-    gtk.gtk_menu_shell_append(this.menuShell, ctypes.cast(menuItem, gtk.GtkWidget.ptr));
+    var menuItemWidget = ctypes.cast(menuItem, gtk.GtkWidget.ptr);
+    if (it.inFront)
+      gtk.gtk_menu_shell_prepend(this.menuShell, menuItemWidget);
+    else
+      gtk.gtk_menu_shell_append(this.menuShell, menuItemWidget);
 
     function capitalizeFirst(str) {
       return str.charAt(0).toUpperCase() + str.substring(1);
     }
 
-    let cbName = "menuItem"+capitalizeFirst(itemName)+capitalizeFirst(action);
-    log.debug("cbName="+cbName);
-    this.callbacks[cbName] = gobject.GCallback_t(callback); // void return, no sentinel
-    gobject.g_signal_connect(menuItem, action,
+    let cbName = "menuItem"+capitalizeFirst(it.itemName)+capitalizeFirst(it.action);
+    if (this.callbacks.hasOwnProperty(cbName))
+      log.warn("callback '"+cbName+"' already registered");
+    else
+      log.debug("cbName="+cbName);
+    this.callbacks[cbName] = gobject.GCallback_t(it.callback); // void return, no sentinel
+    gobject.g_signal_connect(menuItem, it.action,
                              firetray.PopupMenu.callbacks[cbName], null);
+
+    return menuItem;
+  },
+
+  prependAppIndicatorItems: function() {
+    this.menuShowHide.sep = gtk.gtk_separator_menu_item_new();
+    gtk.gtk_menu_shell_prepend(this.menuShell, ctypes.cast(this.menuShowHide.sep,
+                                                           gtk.GtkWidget.ptr));
+    this.menuShowHide.item = this.addItem({
+      itemName:"ShowHide", iconName:"gtk-go-down", action:"activate", callback:
+      firetray.Handler.showHideAllWindows, inFront: true});
+    this.menuShowHide.tip = this.createAndAddItemToMenuAt(0);
+    gtk.gtk_widget_set_sensitive(
+      ctypes.cast(this.menuShowHide.tip, gtk.GtkWidget.ptr), false);
   },
 
   popup: function(icon, button, activateTime, menu) {
@@ -116,9 +143,10 @@ firetray.PopupMenu = {
   // we'll just have to show the menuItems
   addWindowItem: function(xid) { // on registerWindow
     log.debug("addWindowItem");
-    var menuItemWindow = this.createAndAddItemToMenu();
+    var menuItemWindow = this.createAndAddItemToMenuAt(
+      this.MENU_ITEM_WINDOWS_POSITION);
     firetray.Handler.gtkPopupMenuWindowItems.insert(xid, menuItemWindow);
-    this.setWindowItemLabel(menuItemWindow, xid.toString()); // default to xid
+    this.setItemLabel(menuItemWindow, xid.toString()); // default to xid
 
     let callback = gobject.GCallback_t(
       function(){firetray.Handler.showWindow(xid);}); // void return, no sentinel
@@ -128,10 +156,11 @@ firetray.PopupMenu = {
     log.debug("added gtkPopupMenuWindowItems: "+firetray.Handler.gtkPopupMenuWindowItems.count);
   },
 
-  createAndAddItemToMenu: function() {
+  createAndAddItemToMenuAt: function(pos) {
     var menuItem = gtk.gtk_image_menu_item_new();
-    gtk.gtk_menu_shell_prepend(this.menuShell, ctypes.cast(menuItem,
-                                                           gtk.GtkWidget.ptr));
+    gtk.gtk_menu_shell_insert(this.menuShell,
+                              ctypes.cast(menuItem, gtk.GtkWidget.ptr),
+                              pos);
     return menuItem;
   },
 
@@ -158,7 +187,7 @@ firetray.PopupMenu = {
     log.debug("showWindowItem");
     let menuItemWindow = firetray.Handler.gtkPopupMenuWindowItems.get(xid);
     this.showItem(menuItemWindow);
-    this.setWindowItemLabel(menuItemWindow, firetray.Window.getWindowTitle(xid));
+    this.setItemLabel(menuItemWindow, firetray.Window.getWindowTitle(xid));
     this.showWindowSeparator();
   },
 
@@ -166,7 +195,7 @@ firetray.PopupMenu = {
     gtk.gtk_widget_show(ctypes.cast(menuItem, gtk.GtkWidget.ptr));
   },
 
-  setWindowItemLabel: function(menuItem, label) {
+  setItemLabel: function(menuItem, label) {
     log.debug("about to set title: "+label);
     if (label)
       gtk.gtk_menu_item_set_label(ctypes.cast(menuItem, gtk.GtkMenuItem.ptr), label);
diff --git a/src/modules/linux/FiretrayStatusIcon.jsm b/src/modules/linux/FiretrayStatusIcon.jsm
index 04a9afb..1b5755c 100644
--- a/src/modules/linux/FiretrayStatusIcon.jsm
+++ b/src/modules/linux/FiretrayStatusIcon.jsm
@@ -6,19 +6,12 @@ const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/ctypes.jsm");
-Cu.import("resource://firetray/ctypes/linux/cairo.jsm");
-Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
 Cu.import("resource://firetray/ctypes/linux/gdk.jsm");
 Cu.import("resource://firetray/ctypes/linux/gio.jsm");
 Cu.import("resource://firetray/ctypes/linux/glib.jsm");
-Cu.import("resource://firetray/ctypes/linux/gtk.jsm");
-Cu.import("resource://firetray/ctypes/linux/pango.jsm");
-Cu.import("resource://firetray/ctypes/linux/pangocairo.jsm");
+Cu.import("resource://firetray/ctypes/linux/gobject.jsm");
 Cu.import("resource://firetray/commons.js");
-firetray.Handler.subscribeLibsForClosing([cairo, gobject, gdk, gio, gtk, pango, pangocairo]);
+firetray.Handler.subscribeLibsForClosing([gdk, gio, glib, gobject]);
 
 let log = firetray.Logging.getLogger("firetray.StatusIcon");
 
@@ -27,84 +20,48 @@ if ("undefined" == typeof(firetray.Handler))
 
 
 firetray.StatusIcon = {
-  MIN_FONT_SIZE: 4,
-  FILENAME_BLANK: null,
-
   initialized: false,
   callbacks: {}, // pointers to JS functions. MUST LIVE DURING ALL THE EXECUTION
-  trayIcon: null,
-  themedIconApp: null,
-  themedIconNewMail: null,
   prefAppIconNames: null,
   prefNewMailIconNames: null,
   defaultAppIconName: null,
   defaultNewMailIconName: null,
-  inidicator: null,
 
   init: function() {
-    this.FILENAME_BLANK = firetray.Utils.chromeToPath(
-      "chrome://firetray/skin/icons/blank-icon.png");
-
-    Cu.import("resource://firetray/linux/FiretrayGtkIcons.jsm");
-    firetray.GtkIcons.init();
     this.defineIconNames();
-    this.loadThemedIcons();
 
-    Cu.import("resource://firetray/linux/FiretrayPopupMenu.jsm");
-    if (!firetray.PopupMenu.init())
-      return false;
-
-    // FIXME: we may want to split into 2 separate modules: GtkStatusIcon and
-    // AppIndicator.
+    // PopupMenu g_connect's some Handler functions. As these are overridden is
+    // StatusIcon implementations, PopupMenu must be initialized *after*
+    // implemenations are imported.
     Cu.import("resource://firetray/ctypes/linux/appindicator.jsm");
     if (appind3.available() && this.dbusNotificationWatcherReady()) {
-      firetray.Handler.subscribeLibsForClosing([appind3]);
-      this.indicatorInit();
+      /* FIXME: Ubuntu14.04/Unity: successfully closing appind3 crashes FF/TB
+       during exit, in Ubuntu's unity-menubar.patch's code.
+       https://bugs.launchpad.net/ubuntu/+source/firefox/+bug/1393256 */
+      // firetray.Handler.subscribeLibsForClosing([appind3]);
+      Cu.import("resource://firetray/linux/FiretrayAppIndicator.jsm");
     } else {
-      this.trayIcon = gtk.gtk_status_icon_new();
-      firetray.Handler.setIconImageDefault();
-      firetray.Handler.setIconTooltipDefault();
-      this.addCallbacks();
+      Cu.import("resource://firetray/linux/FiretrayGtkStatusIcon.jsm");
     }
 
+    Cu.import("resource://firetray/linux/FiretrayPopupMenu.jsm");
+    if (!firetray.PopupMenu.init())
+      return false;
+
+    if (!firetray.StatusIcon.initImpl())
+      return false;
+
     this.initialized = true;
     return true;
   },
 
   shutdown: function() {
     log.debug("Disabling StatusIcon");
+    firetray.StatusIcon.shutdownImpl();
     firetray.PopupMenu.shutdown();
-    // FIXME: should destroy/hide icon here
-    firetray.GtkIcons.shutdown();
     this.initialized = false;
   },
 
-  indicatorInit: function() {
-    this.indicator = appind3.app_indicator_new(
-      FIRETRAY_APPINDICATOR_ID,
-      'firefox',
-      appind3.APP_INDICATOR_CATEGORY_COMMUNICATIONS
-    );
-    appind3.app_indicator_set_status(this.indicator, appind3.APP_INDICATOR_STATUS_ACTIVE);
-    appind3.app_indicator_set_menu(this.indicator, firetray.PopupMenu.menu); // mandatory
-    log.warn("indicator="+this.indicator);
-    /*
-     let gval = new gobject.gboolean;
-     gobject.g_object_get(
-     ctypes.cast(this.indicator, gobject.gpointer),
-     "connected",
-     gval.address(),
-     ctypes.voidptr_t(null)
-     );
-     log.warn("gval="+gval+" true? "+!firetray.js.strEquals(gval, gobject.FALSE));
-     */
-    this.callbacks.indicator = appind3.AppIndicatorConnectionChangedCb_t(
-      firetray.StatusIcon.onAppIndicatorConnectionChanged); // void return, no sentinel
-    gobject.g_signal_connect(this.indicator, "connection-changed",
-                             firetray.StatusIcon.callbacks.indicator, null);
-    log.warn("status="+appind3.app_indicator_get_status(this.indicator));
-  },
-
   defineIconNames: function() {
     this.prefAppIconNames = (function() {
       if (firetray.Handler.inMailApp) {
@@ -121,126 +78,20 @@ firetray.StatusIcon = {
     this.defaultNewMailIconName = "mail-unread";
   },
 
-  loadThemedIcons: function() {
-    if (firetray.Handler.inMailApp) {
-      let newMailIconNames = this.getNewMailIconNames();
-      if (this.themedIconNewMail) gobject.g_object_unref(this.themedIconNewMail);
-      this.themedIconNewMail = this.initThemedIcon(newMailIconNames);
-    }
-    let appIconNames = this.getAppIconNames();
-    if (this.themedIconApp) gobject.g_object_unref(this.themedIconApp);
-    this.themedIconApp = this.initThemedIcon(appIconNames);
-  },
-
-  loadImageCustom: function() { }, // done in setIconImageCustom
-
   getAppIconNames: function() {
-    let appIconNames = firetray.Utils.getArrayPref(this.prefAppIconNames);
-    appIconNames.push(this.defaultAppIconName);
+    let appIconNames = firetray.Utils.getArrayPref(
+      firetray.StatusIcon.prefAppIconNames);
+    appIconNames.push(firetray.StatusIcon.defaultAppIconName);
     return appIconNames;
   },
   getNewMailIconNames: function() {
-    let newMailIconNames = firetray.Utils.getArrayPref(this.prefNewMailIconNames);
-    newMailIconNames.push(this.defaultNewMailIconName);
+    let newMailIconNames = firetray.Utils.getArrayPref(
+      firetray.StatusIcon.prefNewMailIconNames);
+    newMailIconNames.push(firetray.StatusIcon.defaultNewMailIconName);
     return newMailIconNames;
   },
 
-  initThemedIcon: function(names) {
-    if (!firetray.js.isArray(names)) throw new TypeError();
-    log.debug("themedIconNames="+names);
-    let namesLen = names.length;
-    log.debug("themedIconNamesLen="+namesLen);
-    let themedIconNames = ctypes.char.ptr.array(namesLen)();
-    for (let i=0; i<namesLen; ++i)
-      themedIconNames[i] = ctypes.char.array()(names[i]);
-    log.debug("themedIconNames="+themedIconNames);
-    let themedIcon = gio.g_themed_icon_new_from_names(themedIconNames, namesLen);
-    log.debug("themedIcon="+themedIcon);
-    return themedIcon;
-  },
-
-  addCallbacks: function() {
-    /* NOTE: here we do use a function handler (instead of a function
-     definition) because we need the args passed to it ! As a consequence, we
-     need to abandon 'this' in PopupMenu.popup() */
-    this.callbacks.menuPopup = gtk.GCallbackMenuPopup_t(firetray.PopupMenu.popup); // void return, no sentinel
-    gobject.g_signal_connect(this.trayIcon, "popup-menu",
-      firetray.StatusIcon.callbacks.menuPopup, firetray.PopupMenu.menu);
-    this.callbacks.onScroll = gtk.GCallbackOnScroll_t(
-      firetray.StatusIcon.onScroll, null, FIRETRAY_CB_SENTINEL);
-    gobject.g_signal_connect(this.trayIcon, "scroll-event",
-      firetray.StatusIcon.callbacks.onScroll, null);
-
-    log.debug("showHideAllWindows: "+firetray.Handler.hasOwnProperty("showHideAllWindows"));
-    this.callbacks.iconActivate = gtk.GCallbackStatusIconActivate_t(
-      firetray.StatusIcon.onClick, null, FIRETRAY_CB_SENTINEL);
-    let handlerId = gobject.g_signal_connect(firetray.StatusIcon.trayIcon,
-      "activate", firetray.StatusIcon.callbacks.iconActivate, null);
-    log.debug("g_connect activate="+handlerId);
-
-    this.callbacks.iconMiddleClick = gtk.GCallbackStatusIconMiddleClick_t(
-      firetray.Handler.activateLastWindowCb, null, FIRETRAY_CB_SENTINEL);
-    handlerId = gobject.g_signal_connect(firetray.StatusIcon.trayIcon,
-      "button-press-event", firetray.StatusIcon.callbacks.iconMiddleClick, null);
-    log.debug("g_connect middleClick="+handlerId);
-  },
-
-  onScroll: function(icon, event, data) {
-    if (!firetray.Utils.prefService.getBoolPref("scroll_hides"))
-      return false;
-
-    let iconGpointer = ctypes.cast(icon, gobject.gpointer);
-    let gdkEventScroll = ctypes.cast(event, gdk.GdkEventScroll.ptr);
-    let scroll_mode = firetray.Utils.prefService.getCharPref("scroll_mode");
-
-    let direction = gdkEventScroll.contents.direction;
-    switch(direction) {
-    case gdk.GDK_SCROLL_UP:
-      log.debug("SCROLL UP");
-      if (scroll_mode === "down_hides")
-        firetray.Handler.showAllWindows();
-      else if (scroll_mode === "up_hides")
-        firetray.Handler.hideAllWindows();
-      break;
-    case gdk.GDK_SCROLL_DOWN:
-      log.debug("SCROLL DOWN");
-      if (scroll_mode === "down_hides")
-        firetray.Handler.hideAllWindows();
-      else if (scroll_mode === "up_hides")
-        firetray.Handler.showAllWindows();
-      break;
-    default:
-      log.error("SCROLL UNKNOWN");
-    }
-
-    let stopPropagation = false;
-    return stopPropagation;
-  },
-
-  onClick: function(gtkStatusIcon, userData) {
-    firetray.Handler.showHideAllWindows();
-    let stopPropagation = true;
-    return stopPropagation;
-  },
-
-  setIconImageFromFile: function(filename) {
-    if (!firetray.StatusIcon.trayIcon)
-      log.error("Icon missing");
-    log.debug(filename);
-    gtk.gtk_status_icon_set_from_file(firetray.StatusIcon.trayIcon,
-                                      filename);
-  },
-
-  setIconImageFromGIcon: function(gicon) {
-    if (!firetray.StatusIcon.trayIcon || !gicon)
-      log.error("Icon missing");
-    log.debug(gicon);
-    gtk.gtk_status_icon_set_from_gicon(firetray.StatusIcon.trayIcon, gicon);
-  },
-
-  onAppIndicatorConnectionChanged: function(indicator, connected, data) {
-    log.warn("AppIndicator connection-changed: "+connected);
-  },
+  loadImageCustom: function() { }, // done in setIconImageCustom
 
   dbusNotificationWatcherReady: function() {
     let watcherReady = false;
@@ -285,44 +136,37 @@ firetray.StatusIcon = {
     }
 
     return watcherReady;
-  }
+  },
 
-}; // firetray.StatusIcon
+  onScroll: function(direction) {
+    if (!firetray.Utils.prefService.getBoolPref("scroll_hides"))
+      return false;
 
-firetray.Handler.setIconImageDefault = function() {
-  log.debug("setIconImageDefault");
-  if (!firetray.StatusIcon.themedIconApp)
-    throw "Default application themed icon not set";
-  let appIconType = firetray.Utils.prefService.getIntPref("app_icon_type");
-  if (appIconType === FIRETRAY_APPLICATION_ICON_TYPE_THEMED)
-    firetray.StatusIcon.setIconImageFromGIcon(firetray.StatusIcon.themedIconApp);
-  else if (appIconType === FIRETRAY_APPLICATION_ICON_TYPE_CUSTOM)
-    firetray.Handler.setIconImageCustom("app_icon_custom");
-};
+    let scroll_mode = firetray.Utils.prefService.getCharPref("scroll_mode");
+    switch(direction) {
+    case gdk.GDK_SCROLL_UP:
+      log.debug("SCROLL UP");
+      if (scroll_mode === "down_hides")
+        firetray.Handler.showAllWindows();
+      else if (scroll_mode === "up_hides")
+        firetray.Handler.hideAllWindows();
+      break;
+    case gdk.GDK_SCROLL_DOWN:
+      log.debug("SCROLL DOWN");
+      if (scroll_mode === "down_hides")
+        firetray.Handler.hideAllWindows();
+      else if (scroll_mode === "up_hides")
+        firetray.Handler.showAllWindows();
+      break;
+    default:
+      log.error("SCROLL UNKNOWN");
+    }
 
-firetray.Handler.setIconImageNewMail = function() {
-  firetray.StatusIcon.setIconImageFromGIcon(firetray.StatusIcon.themedIconNewMail);
-};
+    return true;
+  }
 
-firetray.Handler.setIconImageCustom = function(prefname) {
-  let prefCustomIconPath = firetray.Utils.prefService.getCharPref(prefname);
-  firetray.StatusIcon.setIconImageFromFile(prefCustomIconPath);
-};
+}; // firetray.StatusIcon
 
-// GTK bug: Gdk-CRITICAL **: IA__gdk_window_get_root_coords: assertion `GDK_IS_WINDOW (window)' failed
-firetray.Handler.setIconTooltip = function(toolTipStr) {
-  if (!firetray.StatusIcon.trayIcon)
-    return false;
-
-  try {
-    gtk.gtk_status_icon_set_tooltip_text(firetray.StatusIcon.trayIcon,
-                                         toolTipStr);
-  } catch (x) {
-    log.error(x);
-    return false;
-  }
-  return true;
-};
 
 firetray.Handler.setIconTooltipDefault = function() {
   if (!this.appName)
@@ -330,117 +174,4 @@ firetray.Handler.setIconTooltipDefault = function() {
   this.setIconTooltip(this.appName);
 };
 
-firetray.Handler.setIconText = function(text, color) { // FIXME: function too long
-  log.debug("setIconText, color="+color);
-  if (typeof(text) != "string")
-    throw new TypeError();
-
-  try {
-    // build background from image
-    let specialIcon = gdk.gdk_pixbuf_new_from_file(
-      firetray.StatusIcon.FILENAME_BLANK, null); // GError **error);
-    let dest = gdk.gdk_pixbuf_copy(specialIcon);
-    let w = gdk.gdk_pixbuf_get_width(specialIcon);
-    let h = gdk.gdk_pixbuf_get_height(specialIcon);
-
-    // prepare colors/alpha
-    let colorMap = gdk.gdk_screen_get_system_colormap(gdk.gdk_screen_get_default());
-    let visual = gdk.gdk_colormap_get_visual(colorMap);
-    let visualDepth = visual.contents.depth;
-    log.debug("colorMap="+colorMap+" visual="+visual+" visualDepth="+visualDepth);
-    let fore = new gdk.GdkColor;
-    fore.pixel = fore.red = fore.green = fore.blue = 0;
-    let alpha  = new gdk.GdkColor;
-    alpha.pixel = alpha.red = alpha.green = alpha.blue = 0xFFFF;
-    if (!fore || !alpha)
-      log.warn("Undefined GdkColor fore or alpha");
-    gdk.gdk_color_parse(color, fore.address());
-    if(fore.red == alpha.red && fore.green == alpha.green && fore.blue == alpha.blue) {
-      alpha.red=0; // make sure alpha is different from fore
-    }
-    gdk.gdk_colormap_alloc_color(colorMap, fore.address(), true, true);
-    gdk.gdk_colormap_alloc_color(colorMap, alpha.address(), true, true);
-
-    // build pixmap with rectangle
-    let pm = gdk.gdk_pixmap_new(null, w, h, visualDepth);
-    let pmDrawable = ctypes.cast(pm, gdk.GdkDrawable.ptr);
-    let cr = gdk.gdk_cairo_create(pmDrawable);
-    gdk.gdk_cairo_set_source_color(cr, alpha.address());
-    cairo.cairo_rectangle(cr, 0, 0, w, h);
-    cairo.cairo_set_source_rgb(cr, 1, 1, 1);
-    cairo.cairo_fill(cr);
-
-    // build text
-    let scratch = gtk.gtk_window_new(gtk.GTK_WINDOW_TOPLEVEL);
-    let layout = gtk.gtk_widget_create_pango_layout(scratch, null);
-    gtk.gtk_widget_destroy(scratch);
-    let fnt = pango.pango_font_description_from_string("Sans 18");
-    pango.pango_font_description_set_weight(fnt,pango.PANGO_WEIGHT_SEMIBOLD);
-    pango.pango_layout_set_spacing(layout,0);
-    pango.pango_layout_set_font_description(layout, fnt);
-    log.debug("layout="+layout);
-    log.debug("text="+text);
-    pango.pango_layout_set_text(layout, text,-1);
-    let tw = new ctypes.int;
-    let th = new ctypes.int;
-    let sz;
-    let border = 4;
-    pango.pango_layout_get_pixel_size(layout, tw.address(), th.address());
-    log.debug("tw="+tw.value+" th="+th.value);
-    // fit text to the icon by decreasing font size
-    while ( tw.value > (w - border) || th.value > (h - border) ) {
-      sz = pango.pango_font_description_get_size(fnt);
-      if(sz < firetray.StatusIcon.MIN_FONT_SIZE) {
-        sz = firetray.StatusIcon.MIN_FONT_SIZE;
-        break;
-      }
-      sz -= pango.PANGO_SCALE;
-      pango.pango_font_description_set_size(fnt,sz);
-      pango.pango_layout_set_font_description(layout, fnt);
-      pango.pango_layout_get_pixel_size(layout, tw.address(), th.address());
-    }
-    log.debug("tw="+tw.value+" th="+th.value);
-    pango.pango_font_description_free(fnt);
-    // center text
-    let px = (w-tw.value)/2;
-    let py = (h-th.value)/2;
-
-    // draw text on pixmap
-    gdk.gdk_cairo_set_source_color(cr, fore.address());
-    cairo.cairo_move_to(cr, px, py);
-    pangocairo.pango_cairo_show_layout(cr, layout);
-    cairo.cairo_destroy(cr);
-    gobject.g_object_unref(layout);
-
-    let buf = gdk.gdk_pixbuf_get_from_drawable(null, pmDrawable, null, 0, 0, 0, 0, w, h);
-    gobject.g_object_unref(pm);
-    log.debug("alpha="+alpha);
-    let alphaRed = gobject.guint16(alpha.red);
-    let alphaRed_guchar = ctypes.cast(alphaRed, gobject.guchar);
-    let alphaGreen = gobject.guint16(alpha.green);
-    let alphaGreen_guchar = ctypes.cast(alphaGreen, gobject.guchar);
-    let alphaBlue = gobject.guint16(alpha.blue);
-    let alphaBlue_guchar = ctypes.cast(alphaBlue, gobject.guchar);
-    let bufAlpha = gdk.gdk_pixbuf_add_alpha(buf, true, alphaRed_guchar, alphaGreen_guchar, alphaBlue_guchar);
-    gobject.g_object_unref(buf);
-
-    // merge the rendered text on top
-    gdk.gdk_pixbuf_composite(bufAlpha,dest,0,0,w,h,0,0,1,1,gdk.GDK_INTERP_BILINEAR,255);
-    gobject.g_object_unref(bufAlpha);
-
-    log.debug("gtk_status_icon_set_from_pixbuf="+dest);
-    gtk.gtk_status_icon_set_from_pixbuf(firetray.StatusIcon.trayIcon, dest);
-  } catch (x) {
-    log.error(x);
-    return false;
-  }
-
-  return true;
-};
-
-firetray.Handler.setIconVisibility = function(visible) {
-  if (!firetray.StatusIcon.trayIcon)
-    return false;
-  gtk.gtk_status_icon_set_visible(firetray.StatusIcon.trayIcon, visible);
-  return true;
-};
+firetray.Handler.setIconTooltip = function(toolTipStr) { };
diff --git a/src/modules/winnt/FiretrayStatusIcon.jsm b/src/modules/winnt/FiretrayStatusIcon.jsm
index cab03f5..8d9b63d 100644
--- a/src/modules/winnt/FiretrayStatusIcon.jsm
+++ b/src/modules/winnt/FiretrayStatusIcon.jsm
@@ -81,8 +81,6 @@ firetray.StatusIcon = {
     return true;
   },
 
-  loadThemedIcons: function() { },
-
   loadImages: function() {
     let topmost = firetray.Handler.getWindowInterface(
       Services.wm.getMostRecentWindow(null), "nsIBaseWindow");

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-mozext/firetray.git



More information about the Pkg-mozext-commits mailing list