[Pkg-mozext-commits] [firetray] 11/84: * un-/register windows (begin) => refactoring * filter icon events (begin)

David Prévot taffit at moszumanska.debian.org
Sun Jul 20 01:42:41 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 d52245cc87e35e5eca30d4e1c77f2c16afa77ebd
Author: foudfou <foudil.newbie+git at gmail.com>
Date:   Sun Nov 24 14:25:27 2013 +0100

    * un-/register windows (begin) => refactoring
    * filter icon events (begin)
---
 src/modules/FiretrayWindow.jsm           |   32 +
 src/modules/ctypes/winnt/win32.jsm       |   29 +
 src/modules/linux/FiretrayWindow.jsm     | 1115 +++++++++++++++---------------
 src/modules/winnt/FiretrayStatusIcon.jsm |   65 +-
 src/modules/winnt/FiretrayWin32.jsm      |    9 -
 src/modules/winnt/FiretrayWindow.jsm     |   90 ++-
 6 files changed, 707 insertions(+), 633 deletions(-)

diff --git a/src/modules/FiretrayWindow.jsm b/src/modules/FiretrayWindow.jsm
new file mode 100644
index 0000000..98bb1a5
--- /dev/null
+++ b/src/modules/FiretrayWindow.jsm
@@ -0,0 +1,32 @@
+/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// https://developer.mozilla.org/en/Code_snippets/Preferences
+
+var EXPORTED_SYMBOLS = [ "FiretrayWindow" ];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://firetray/commons.js");
+
+let log = firetray.Logging.getLogger("firetray.FiretrayWindow");
+
+if ("undefined" == typeof(firetray.Handler))
+  log.error("This module MUST be imported from/after FiretrayHandler !");
+
+function FiretrayWindow () {}
+FiretrayWindow.prototype = {
+
+  updateVisibility: function(winId, visibility) {
+    let win = firetray.Handler.windows[winId];
+    if (win.visible === visibility)
+      log.warn("window (winId="+winId+") was already visible="+win.visible);
+
+    firetray.Handler.visibleWindowsCount = visibility ?
+      firetray.Handler.visibleWindowsCount + 1 :
+      firetray.Handler.visibleWindowsCount - 1 ;
+
+    win.visible = visibility; // nsIBaseWin.visibility always true :-(
+  },
+
+};
diff --git a/src/modules/ctypes/winnt/win32.jsm b/src/modules/ctypes/winnt/win32.jsm
index 3ffac7c..790ab23 100644
--- a/src/modules/ctypes/winnt/win32.jsm
+++ b/src/modules/ctypes/winnt/win32.jsm
@@ -74,4 +74,33 @@ var win32 = {
   ERROR_INVALID_WINDOW_HANDLE: 1400,
   ERROR_RESOURCE_TYPE_NOT_FOUND: 1813,
 
+  // WinUser.h
+  WM_USER:           0x0400,
+
+  WM_CONTEXTMENU:    0x007B,
+
+  WM_MOUSEFIRST:     0x0200,
+  WM_MOUSEMOVE:      0x0200,
+  WM_LBUTTONDOWN:    0x0201,
+  WM_LBUTTONUP:      0x0202,
+  WM_LBUTTONDBLCLK:  0x0203,
+  WM_RBUTTONDOWN:    0x0204,
+  WM_RBUTTONUP:      0x0205,
+  WM_RBUTTONDBLCLK:  0x0206,
+  WM_MBUTTONDOWN:    0x0207,
+  WM_MBUTTONUP:      0x0208,
+  WM_MBUTTONDBLCLK:  0x0209,
+  WM_MOUSEWHEEL:     0x020A,
+  WM_XBUTTONDOWN:    0x020B,
+  WM_XBUTTONUP:      0x020C,
+  WM_XBUTTONDBLCLK:  0x020D,
+  WM_MOUSELAST:      0x020D,
+  WM_MOUSELAST:      0x020A,
+
 };
+
+// ShellAPI.h
+let nin_select = win32.WM_USER + 0;
+win32.NIN_SELECT = nin_select;
+win32.NINF_KEY = 0x1;
+win32.NIN_KEYSELECT = (win32.NIN_SELECT | win32.NINF_KEY);
diff --git a/src/modules/linux/FiretrayWindow.jsm b/src/modules/linux/FiretrayWindow.jsm
index 12b498c..1986776 100644
--- a/src/modules/linux/FiretrayWindow.jsm
+++ b/src/modules/linux/FiretrayWindow.jsm
@@ -22,6 +22,7 @@ Cu.import("resource://firetray/ctypes/linux/gdk.jsm");
 Cu.import("resource://firetray/ctypes/linux/gtk.jsm");
 Cu.import("resource://firetray/ctypes/linux/libc.jsm");
 Cu.import("resource://firetray/ctypes/linux/x11.jsm");
+Cu.import("resource://firetray/FiretrayWindow.jsm");
 Cu.import("resource://firetray/commons.js");
 firetray.Handler.subscribeLibsForClosing([gobject, gdk, gtk, libc, x11, glib]);
 
@@ -57,606 +58,594 @@ firetray.Handler.gdkWindows              = new ctypesMap(gdk.GdkWindow.ptr),
 firetray.Handler.gtkPopupMenuWindowItems = new ctypesMap(gtk.GtkImageMenuItem.ptr),
 
 
-firetray.Window = {
-  signals: {'focus-in': {callback: {}, handler: {}}},
+firetray.Window = new FiretrayWindow();
 
-  init: function() {
-    let gtkVersionCheck = gtk.gtk_check_version(
-      gtk.FIRETRAY_REQUIRED_GTK_MAJOR_VERSION,
-      gtk.FIRETRAY_REQUIRED_GTK_MINOR_VERSION,
-      gtk.FIRETRAY_REQUIRED_GTK_MICRO_VERSION
-    );
-    if (!gtkVersionCheck.isNull())
-      log.error("gtk_check_version="+gtkVersionCheck.readString());
+firetray.Window.signals = {'focus-in': {callback: {}, handler: {}}};
 
-    if (firetray.Handler.isChatEnabled()) {
-      Cu.import("resource://firetray/FiretrayChat.jsm");
-      Cu.import("resource://firetray/linux/FiretrayChatStatusIcon.jsm");
-    }
+firetray.Window.init = function() {
+  let gtkVersionCheck = gtk.gtk_check_version(
+    gtk.FIRETRAY_REQUIRED_GTK_MAJOR_VERSION,
+    gtk.FIRETRAY_REQUIRED_GTK_MINOR_VERSION,
+    gtk.FIRETRAY_REQUIRED_GTK_MICRO_VERSION
+  );
+  if (!gtkVersionCheck.isNull())
+    log.error("gtk_check_version="+gtkVersionCheck.readString());
 
-    this.initialized = true;
-  },
-
-  shutdown: function() {
-    this.initialized = false;
-  },
-
-  /**
-   * Iterate over all Gtk toplevel windows to find a window. We rely on
-   * Service.wm to watch windows correctly: we should find only one window.
-   *
-   * @author Nils Maier (stolen from MiniTrayR)
-   * @param window nsIDOMWindow from Services.wm
-   * @return a gtk.GtkWindow.ptr
-   */
-  getGtkWindowFromChromeWindow: function(window) {
-    let baseWindow = window
-          .QueryInterface(Ci.nsIInterfaceRequestor)
-          .getInterface(Ci.nsIWebNavigation)
-          .QueryInterface(Ci.nsIBaseWindow);
-
-    // Tag the base window
-    let oldTitle = baseWindow.title;
-    log.debug("oldTitle="+oldTitle);
-    baseWindow.title = Services2.uuid.generateUUID().toString();
-
-    try {
-      // Search the window by the *temporary* title
-      let widgets = gtk.gtk_window_list_toplevels();
-      let that = this;
-      let findGtkWindowByTitleCb = gobject.GFunc_t(that._findGtkWindowByTitle);
-      var userData = new _find_data_t(
-        ctypes.char.array()(baseWindow.title),
-        null
-      ).address();
-      log.debug("userData="+userData);
-      gobject.g_list_foreach(widgets, findGtkWindowByTitleCb, userData);
-      gobject.g_list_free(widgets);
-
-      if (userData.contents.outWindow.isNull())
-        throw new Error("Window not found!");
-
-      log.debug("found window: "+userData.contents.outWindow);
-    } catch (x) {
-      log.error(x);
-    } finally {
-      // Restore
-      baseWindow.title = oldTitle;
-    }
+  if (firetray.Handler.isChatEnabled()) {
+    Cu.import("resource://firetray/FiretrayChat.jsm");
+    Cu.import("resource://firetray/linux/FiretrayChatStatusIcon.jsm");
+  }
 
-    return userData.contents.outWindow;
-  },
-
-  /**
-   * compares a GtkWindow's title with a string passed in userData
-   * @param gtkWidget: GtkWidget from gtk_window_list_toplevels()
-   * @param userData: _find_data_t
-   */
-  _findGtkWindowByTitle: function(gtkWidget, userData) {
-    let data = ctypes.cast(userData, _find_data_t.ptr);
-    let inTitle = data.contents.inTitle;
-
-    let gtkWin = ctypes.cast(gtkWidget, gtk.GtkWindow.ptr);
-    let winTitle = gtk.gtk_window_get_title(gtkWin);
-
-    if (!winTitle.isNull()) {
-      log.debug(inTitle+" = "+winTitle);
-      if (libc.strcmp(inTitle, winTitle) == 0)
-        data.contents.outWindow = gtkWin;
-    }
-  },
-
-  getGdkWindowFromGtkWindow: function(gtkWin) {
-    try {
-      let gtkWid = ctypes.cast(gtkWin, gtk.GtkWidget.ptr);
-      return gtk.gtk_widget_get_window(gtkWid);
-    } catch (x) {
-      log.error(x);
-    }
-    return null;
-  },
-
-  getXIDFromGdkWindow: function(gdkWin) {
-    return gdk.gdk_x11_drawable_get_xid(ctypes.cast(gdkWin, gdk.GdkDrawable.ptr));
-  },
-
-  getXIDFromGtkWidget: function(gtkWid) {
-    let gdkWin = gtk.gtk_widget_get_window(gtkWid);
-    return gdk.gdk_x11_drawable_get_xid(ctypes.cast(gdkWin, gdk.GdkDrawable.ptr));
-  },
-
-  addrPointedByInHex: function(ptr) {
-    return "0x"+ctypes.cast(ptr, ctypes.uintptr_t.ptr).contents.toString(16);
-  },
-
-  getGdkWindowFromNativeHandle: function(nativeHandle) {
-    let gdkw = new gdk.GdkWindow.ptr(ctypes.UInt64(nativeHandle)); // a new pointer to the GdkWindow
-    gdkw = gdk.gdk_window_get_toplevel(gdkw);
-    log.debug("gdkw="+gdkw+" *gdkw="+this.addrPointedByInHex(gdkw));
-    return gdkw;
-  },
-
-  getGtkWindowFromGdkWindow: function(gdkWin) {
-    let gptr = new gobject.gpointer;
-    gdk.gdk_window_get_user_data(gdkWin, gptr.address());
-    log.debug("gptr="+gptr+" *gptr="+this.addrPointedByInHex(gptr));
-    let gtkw = ctypes.cast(gptr, gtk.GtkWindow.ptr);
-    log.debug("gtkw="+gtkw+" *gtkw="+this.addrPointedByInHex(gtkw));
-    return gtkw;
-  },
-
-  /* consider using getXIDFromChromeWindow() if you only need the XID */
-  getWindowsFromChromeWindow: function(win) {
-    let baseWin = firetray.Handler.getWindowInterface(win, "nsIBaseWindow");
-    let nativeHandle = baseWin.nativeHandle; // Moz' private pointer to the GdkWindow
-    log.debug("nativeHandle="+nativeHandle);
-    let gtkWin, gdkWin;
-    if (nativeHandle) { // Gecko 17+
-      gdkWin = firetray.Window.getGdkWindowFromNativeHandle(nativeHandle);
-      gtkWin = firetray.Window.getGtkWindowFromGdkWindow(gdkWin);
-    } else {
-      gtkWin = firetray.Window.getGtkWindowFromChromeWindow(win);
-      gdkWin = firetray.Window.getGdkWindowFromGtkWindow(gtkWin);
-    }
-    let xid = firetray.Window.getXIDFromGdkWindow(gdkWin);
-    log.debug("XID="+xid);
-    return [baseWin, gtkWin, gdkWin, xid];
-  },
-
-  getXIDFromChromeWindow: function(win) {
-    for (let xid in firetray.Handler.windows)
-      if (firetray.Handler.windows[xid].chromeWin === win) return xid;
-    log.error("unknown window while lookup");
-    return null;
-  },
+  this.initialized = true;
+};
 
-  unregisterWindowByXID: function(xid) {
-    if (!firetray.Handler.windows.hasOwnProperty(xid)) {
-      log.error("can't unregister unknown window "+xid);
-      return false;
-    }
+firetray.Window.shutdown = function() {
+  this.initialized = false;
+};
 
-    firetray.Window.detachOnFocusInCallback(xid);
-    if (firetray.Handler.isChatEnabled() && firetray.Chat.initialized) {
-      firetray.Chat.detachSelectListeners(firetray.Handler.windows[xid].chromeWin);
-    }
+/**
+ * Iterate over all Gtk toplevel windows to find a window. We rely on
+ * Service.wm to watch windows correctly: we should find only one window.
+ *
+ * @author Nils Maier (stolen from MiniTrayR)
+ * @param window nsIDOMWindow from Services.wm
+ * @return a gtk.GtkWindow.ptr
+ */
+firetray.Window.getGtkWindowFromChromeWindow = function(window) {
+  let baseWindow = window
+        .QueryInterface(Ci.nsIInterfaceRequestor)
+        .getInterface(Ci.nsIWebNavigation)
+        .QueryInterface(Ci.nsIBaseWindow);
 
-    if (!delete firetray.Handler.windows[xid])
-      throw new DeleteError();
-    firetray.Handler.gtkWindows.remove(xid);
-    firetray.Handler.gdkWindows.remove(xid);
-    firetray.Handler.windowsCount -= 1;
-    firetray.Handler.visibleWindowsCount -= 1;
+  // Tag the base window
+  let oldTitle = baseWindow.title;
+  log.debug("oldTitle="+oldTitle);
+  baseWindow.title = Services2.uuid.generateUUID().toString();
 
-    firetray.PopupMenu.removeWindowItem(xid);
+  try {
+    // Search the window by the *temporary* title
+    let widgets = gtk.gtk_window_list_toplevels();
+    let that = this;
+    let findGtkWindowByTitleCb = gobject.GFunc_t(that._findGtkWindowByTitle);
+    var userData = new _find_data_t(
+      ctypes.char.array()(baseWindow.title),
+      null
+    ).address();
+    log.debug("userData="+userData);
+    gobject.g_list_foreach(widgets, findGtkWindowByTitleCb, userData);
+    gobject.g_list_free(widgets);
+
+    if (userData.contents.outWindow.isNull())
+      throw new Error("Window not found!");
+
+    log.debug("found window: "+userData.contents.outWindow);
+  } catch (x) {
+    log.error(x);
+  } finally {
+    // Restore
+    baseWindow.title = oldTitle;
+  }
 
-    log.debug("window "+xid+" unregistered");
-    return true;
-  },
-
-  show: function(xid) {
-    log.debug("show xid="+xid);
-
-    // try to restore previous state. TODO: z-order respected ?
-    firetray.Window.restorePositionAndSize(xid);
-    firetray.Window.restoreStates(xid);
-
-    // better visual effect if visibility set after restorePosition, but some
-    // WMs like compiz seem not to honor position setting if window not visible
-    firetray.Window.setVisibility(xid, true);
-
-    // after show
-    firetray.Window.restoreDesktop(xid);
-    if (firetray.Utils.prefService.getBoolPref('show_activates'))
-      firetray.Window.activate(xid);
-
-    firetray.PopupMenu.hideWindowItemAndSeparatorMaybe(xid);
-    firetray.Handler.showHideIcon();
-  },
-
-  /* FIXME: hiding windows should also hide child windows, like message windows
-   in Thunderbird */
-  hide: function(xid) {
-    log.debug("hide");
-
-    firetray.Window.savePositionAndSize(xid);
-    firetray.Window.saveStates(xid);
-    firetray.Window.saveDesktop(xid);
-
-    firetray.Window.setVisibility(xid, false);
-
-    firetray.PopupMenu.showWindowItem(xid);
-    firetray.Handler.showHideIcon();
-  },
-
-  startupHide: function(xid) {
-    log.debug('startupHide: '+xid);
-
-    // also it seems cleaner, baseWin.visibility=false removes the possibility
-    // to restore the app by calling it from the command line. Not sure why...
-    firetray.Window.setVisibility(xid, false);
-
-    firetray.PopupMenu.showWindowItem(xid);
-    firetray.Handler.showHideIcon();
-  },
-
-  savePositionAndSize: function(xid) {
-    let gx = {}, gy = {}, gwidth = {}, gheight = {};
-    firetray.Handler.windows[xid].baseWin.getPositionAndSize(gx, gy, gwidth, gheight);
-    firetray.Handler.windows[xid].savedX = gx.value;
-    firetray.Handler.windows[xid].savedY = gy.value;
-    firetray.Handler.windows[xid].savedWidth = gwidth.value;
-    firetray.Handler.windows[xid].savedHeight = gheight.value;
-    log.debug("save: gx="+gx.value+", gy="+gy.value+", gwidth="+gwidth.value+", gheight="+gheight.value);
-  },
-
-  restorePositionAndSize: function(xid) {
-    if ("undefined" === typeof(firetray.Handler.windows[xid].savedX))
-      return; // windows[xid].saved* may not be initialized
-
-    log.debug("restore: x="+firetray.Handler.windows[xid].savedX+", y="+firetray.Handler.windows[xid].savedY+", w="+firetray.Handler.windows[xid].savedWidth+", h="+firetray.Handler.windows[xid].savedHeight);
-    firetray.Handler.windows[xid].baseWin.setPositionAndSize(
-      firetray.Handler.windows[xid].savedX,
-      firetray.Handler.windows[xid].savedY,
-      firetray.Handler.windows[xid].savedWidth,
-      firetray.Handler.windows[xid].savedHeight,
-      false); // repaint
-
-    ['savedX', 'savedX', 'savedWidth', 'savedHeight'].forEach(function(element) {
-      delete firetray.Handler.windows[xid][element];
-    });
-  },
-
-  saveStates: function(xid) {
-    let winStates = firetray.Window.getXWindowStates(x11.Window(xid));
-    firetray.Handler.windows[xid].savedStates = winStates;
-    log.debug("save: windowStates="+winStates);
-  },
-
-  // NOTE: fluxbox bug probably: if hidden and restored iconified, then
-  // switching to desktop de-iconifies it ?!
-  restoreStates: function(xid) {
-    let winStates = firetray.Handler.windows[xid].savedStates;
-    log.debug("restored WindowStates: " + winStates);
-
-    if (winStates & FIRETRAY_XWINDOW_HIDDEN) {
-      firetray.Handler.windows[xid].chromeWin.minimize();
-      log.debug("restored minimized");
-    }
+  return userData.contents.outWindow;
+};
 
-    /* we expect the WM to actually show the window *not* minimized once
-     restored */
-    if (firetray.Utils.prefService.getBoolPref('hides_on_minimize'))
-      // help prevent getting iconify event following show()
-      firetray.Handler.windows[xid].chromeWin.restore(); // nsIDOMChromeWindow.idl
+/**
+ * compares a GtkWindow's title with a string passed in userData
+ * @param gtkWidget: GtkWidget from gtk_window_list_toplevels()
+ * @param userData: _find_data_t
+ */
+firetray.Window._findGtkWindowByTitle = function(gtkWidget, userData) {
+  let data = ctypes.cast(userData, _find_data_t.ptr);
+  let inTitle = data.contents.inTitle;
 
-    if (winStates & FIRETRAY_XWINDOW_MAXIMIZED) {
-      firetray.Handler.windows[xid].chromeWin.maximize();
-      log.debug("restored maximized");
-    }
+  let gtkWin = ctypes.cast(gtkWidget, gtk.GtkWindow.ptr);
+  let winTitle = gtk.gtk_window_get_title(gtkWin);
+
+  if (!winTitle.isNull()) {
+    log.debug(inTitle+" = "+winTitle);
+    if (libc.strcmp(inTitle, winTitle) == 0)
+      data.contents.outWindow = gtkWin;
+  }
+};
+
+firetray.Window.getGdkWindowFromGtkWindow = function(gtkWin) {
+  try {
+    let gtkWid = ctypes.cast(gtkWin, gtk.GtkWidget.ptr);
+    return gtk.gtk_widget_get_window(gtkWid);
+  } catch (x) {
+    log.error(x);
+  }
+  return null;
+};
+
+firetray.Window.getXIDFromGdkWindow = function(gdkWin) {
+  return gdk.gdk_x11_drawable_get_xid(ctypes.cast(gdkWin, gdk.GdkDrawable.ptr));
+};
+
+firetray.Window.getXIDFromGtkWidget = function(gtkWid) {
+  let gdkWin = gtk.gtk_widget_get_window(gtkWid);
+  return gdk.gdk_x11_drawable_get_xid(ctypes.cast(gdkWin, gdk.GdkDrawable.ptr));
+};
+
+firetray.Window.addrPointedByInHex = function(ptr) {
+  return "0x"+ctypes.cast(ptr, ctypes.uintptr_t.ptr).contents.toString(16);
+};
 
-    delete firetray.Handler.windows[xid].savedStates;
-  },
+firetray.Window.getGdkWindowFromNativeHandle = function(nativeHandle) {
+  let gdkw = new gdk.GdkWindow.ptr(ctypes.UInt64(nativeHandle)); // a new pointer to the GdkWindow
+  gdkw = gdk.gdk_window_get_toplevel(gdkw);
+  log.debug("gdkw="+gdkw+" *gdkw="+this.addrPointedByInHex(gdkw));
+  return gdkw;
+};
 
-  saveDesktop: function(xid) {
-    if (!firetray.Utils.prefService.getBoolPref('remember_desktop'))
-      return;
+firetray.Window.getGtkWindowFromGdkWindow = function(gdkWin) {
+  let gptr = new gobject.gpointer;
+  gdk.gdk_window_get_user_data(gdkWin, gptr.address());
+  log.debug("gptr="+gptr+" *gptr="+this.addrPointedByInHex(gptr));
+  let gtkw = ctypes.cast(gptr, gtk.GtkWindow.ptr);
+  log.debug("gtkw="+gtkw+" *gtkw="+this.addrPointedByInHex(gtkw));
+  return gtkw;
+};
 
-    let winDesktop = firetray.Window.getXWindowDesktop(x11.Window(xid));
-    firetray.Handler.windows[xid].savedDesktop = winDesktop;
-    log.debug("save: windowDesktop="+winDesktop);
-  },
+/* consider using getXIDFromChromeWindow() if you only need the XID */
+firetray.Window.getWindowsFromChromeWindow = function(win) {
+  let baseWin = firetray.Handler.getWindowInterface(win, "nsIBaseWindow");
+  let nativeHandle = baseWin.nativeHandle; // Moz' private pointer to the GdkWindow
+  log.debug("nativeHandle="+nativeHandle);
+  let gtkWin, gdkWin;
+  if (nativeHandle) { // Gecko 17+
+    gdkWin = firetray.Window.getGdkWindowFromNativeHandle(nativeHandle);
+    gtkWin = firetray.Window.getGtkWindowFromGdkWindow(gdkWin);
+  } else {
+    gtkWin = firetray.Window.getGtkWindowFromChromeWindow(win);
+    gdkWin = firetray.Window.getGdkWindowFromGtkWindow(gtkWin);
+  }
+  let xid = firetray.Window.getXIDFromGdkWindow(gdkWin);
+  log.debug("XID="+xid);
+  return [baseWin, gtkWin, gdkWin, xid];
+};
 
-  restoreDesktop: function(xid) {
-    if (!firetray.Utils.prefService.getBoolPref('remember_desktop'))
-      return;
+firetray.Window.getXIDFromChromeWindow = function(win) {
+  for (let xid in firetray.Handler.windows)
+    if (firetray.Handler.windows[xid].chromeWin === win) return xid;
+  log.error("unknown window while lookup");
+  return null;
+};
 
-    let desktopDest = firetray.Handler.windows[xid].savedDesktop;
-    if (desktopDest === null || "undefined" === typeof(desktopDest)) return;
+firetray.Window.unregisterWindowByXID = function(xid) {
+  if (!firetray.Handler.windows.hasOwnProperty(xid)) {
+    log.error("can't unregister unknown window "+xid);
+    return false;
+  }
 
-    let dataSize = 1;
-    let data = ctypes.long(dataSize);
-    data[0] = desktopDest;
-    this.xSendClientMessgeEvent(xid, x11.current.Atoms._NET_WM_DESKTOP, data, dataSize);
+  firetray.Window.detachOnFocusInCallback(xid);
+  if (firetray.Handler.isChatEnabled() && firetray.Chat.initialized) {
+    firetray.Chat.detachSelectListeners(firetray.Handler.windows[xid].chromeWin);
+  }
 
-    log.debug("restored to desktop: "+desktopDest);
-    delete firetray.Handler.windows[xid].savedDesktop;
-  },
+  if (!delete firetray.Handler.windows[xid])
+    throw new DeleteError();
+  firetray.Handler.gtkWindows.remove(xid);
+  firetray.Handler.gdkWindows.remove(xid);
+  firetray.Handler.windowsCount -= 1;
+  firetray.Handler.visibleWindowsCount -= 1;
 
-  setVisibility: function(xid, visibility) {
-    log.debug("setVisibility="+visibility);
-    let gtkWidget = ctypes.cast(firetray.Handler.gtkWindows.get(xid), gtk.GtkWidget.ptr);
-    if (visibility)
-      gtk.gtk_widget_show_all(gtkWidget);
-    else
-      gtk.gtk_widget_hide(gtkWidget);
+  firetray.PopupMenu.removeWindowItem(xid);
 
-    this.updateVisibility(xid, visibility);
-  },
+  log.debug("window "+xid+" unregistered");
+  return true;
+};
 
-  updateVisibility: function(xid, visibility) {
-    let win = firetray.Handler.windows[xid];
-    if (win.visible === visibility)
-      log.warn("window (xid="+xid+") was already visible="+win.visible);
-
-    firetray.Handler.visibleWindowsCount = visibility ?
-      firetray.Handler.visibleWindowsCount + 1 :
-      firetray.Handler.visibleWindowsCount - 1 ;
-
-    win.visible = visibility; // nsIBaseWin.visibility always true :-(
-  },
-
-  xSendClientMessgeEvent: function(xid, atom, data, dataSize) {
-    let xev = new x11.XClientMessageEvent;
-    xev.type = x11.ClientMessage;
-    xev.window = x11.Window(xid);
-    xev.message_type = atom;
-    xev.format = 32;
-    for (let i=0; i<dataSize; ++i)
-      xev.data[i] = data[i];
-
-    let rootWin = x11.XDefaultRootWindow(x11.current.Display);
-    let propagate = false;
-    let mask = ctypes.long(x11.SubstructureNotifyMask|x11.SubstructureRedirectMask);
-    // fortunately, it's OK not to cast xev. ctypes.cast to a void_t doesn't work (length pb)
-    let status = x11.XSendEvent(x11.current.Display, rootWin, propagate, mask, xev.address());
-    // always returns 1 (BadRequest as a coincidence)
-  },
-
-  /**
-   * raises window on top and give focus.
-   */
-  activate: function(xid) {
-    gtk.gtk_window_present(firetray.Handler.gtkWindows.get(xid));
-    log.debug("window raised");
-  },
-
-  setUrgency: function(xid, urgent) {
-    log.debug("setUrgency: "+urgent);
-    gtk.gtk_window_set_urgency_hint(firetray.Handler.gtkWindows.get(xid), urgent);
-  },
-
-  /**
-   * YOU MUST x11.XFree() THE VARIABLE RETURNED BY THIS FUNCTION
-   * @param xwin: a x11.Window
-   * @param prop: a x11.Atom
-   */
-  getXWindowProperties: function(xwin, prop) {
-    // infos returned by XGetWindowProperty() - FIXME: should be freed ?
-    let actual_type = new x11.Atom;
-    let actual_format = new ctypes.int;
-    let nitems = new ctypes.unsigned_long;
-    let bytes_after = new ctypes.unsigned_long;
-    let prop_value = new ctypes.unsigned_char.ptr;
-
-    let bufSize = XATOMS_EWMH_WM_STATES.length*ctypes.unsigned_long.size;
-    let offset = 0;
-    let res = x11.XGetWindowProperty(
-      x11.current.Display, xwin, prop, offset, bufSize, 0, x11.AnyPropertyType,
-      actual_type.address(), actual_format.address(), nitems.address(),
-      bytes_after.address(), prop_value.address());
-    log.debug("XGetWindowProperty res="+res+", actual_type="+actual_type.value+", actual_format="+actual_format.value+", bytes_after="+bytes_after.value+", nitems="+nitems.value);
-
-    if (!firetray.js.strEquals(res, x11.Success)) {
-      log.error("XGetWindowProperty failed");
-      return [null, null];
-    }
-    if (firetray.js.strEquals(actual_type.value, x11.None)) {
-      log.debug("property not found");
-      return [null, null];
-    }
+firetray.Window.show = function(xid) {
+  log.debug("show xid="+xid);
 
-    log.debug("prop_value="+prop_value+", size="+prop_value.constructor.size);
-    /* If the returned format is 32, the property data will be stored as an
-     array of longs (which in a 64-bit application will be 64-bit values
-     that are padded in the upper 4 bytes). [man XGetWindowProperty] */
-    if (actual_format.value !== 32) {
-      log.error("unsupported format: "+actual_format.value);
-    }
-    log.debug("format OK");
-    var props = ctypes.cast(prop_value, ctypes.unsigned_long.array(nitems.value).ptr);
-    log.debug("props="+props+", size="+props.constructor.size);
-
-    return [props, nitems];
-  },
-
-  /**
-   * check the state of a window by its EWMH window state. This is more
-   * accurate than the chromeWin.windowState or the GdkWindowState which are
-   * based on WM_STATE. For instance, WM_STATE becomes 'Iconic' on virtual
-   * desktop change...
-   */
-  getXWindowStates: function(xwin) {
-    let winStates = 0;
-
-    let [propsFound, nitems] =
-      firetray.Window.getXWindowProperties(xwin, x11.current.Atoms._NET_WM_STATE);
-    log.debug("propsFound, nitems="+propsFound+", "+nitems);
-    if (!propsFound) return 0;
-
-    let maximizedHorz = maximizedVert = false;
-    for (let i=0, len=nitems.value; i<len; ++i) {
-      log.debug("i: "+propsFound.contents[i]);
-      let currentProp = propsFound.contents[i];
-      if (firetray.js.strEquals(currentProp, x11.current.Atoms['_NET_WM_STATE_HIDDEN']))
-        winStates |= FIRETRAY_XWINDOW_HIDDEN;
-      else if (firetray.js.strEquals(currentProp, x11.current.Atoms['_NET_WM_STATE_MAXIMIZED_HORZ']))
-        maximizedHorz = true;
-      else if (firetray.js.strEquals(currentProp, x11.current.Atoms['_NET_WM_STATE_MAXIMIZED_VERT']))
-        maximizedVert = true;
-    }
+  // try to restore previous state. TODO: z-order respected ?
+  firetray.Window.restorePositionAndSize(xid);
+  firetray.Window.restoreStates(xid);
 
-    if (maximizedHorz && maximizedVert)
-      winStates |= FIRETRAY_XWINDOW_MAXIMIZED;
-
-    x11.XFree(propsFound);
-
-    return winStates;
-  },
-
-  getXWindowDesktop: function(xwin) {
-    let desktop = null;
-
-    let [propsFound, nitems] =
-      firetray.Window.getXWindowProperties(xwin, x11.current.Atoms._NET_WM_DESKTOP);
-    log.debug("DESKTOP propsFound, nitems="+propsFound+", "+nitems);
-    if (!propsFound) return null;
-
-    if (firetray.js.strEquals(nitems.value, 0))
-      log.warn("desktop number not found");
-    else if (firetray.js.strEquals(nitems.value, 1))
-      desktop = propsFound.contents[0];
-    else
-      throw new RangeError("more than one desktop found");
-
-    x11.XFree(propsFound);
-
-    return desktop;
-  },
-
-  getWindowTitle: function(xid) {
-    let title = firetray.Handler.windows[xid].baseWin.title;
-    log.debug("|baseWin.title="+title+"|");
-    let tailIndex;
-    tailIndex = title.indexOf(" - Mozilla "+firetray.Handler.appName);
-    if (tailIndex === -1)
-      tailIndex = title.indexOf(" - "+firetray.Handler.appName);
-
-    if (tailIndex !== -1)
-      return title.substring(0, tailIndex);
-    else if (title === "Mozilla "+firetray.Handler.appName)
-      return title;
-    else
-      return null;
-  },
-
-  checkSubscribedEventMasks: function(xid) {
-    let xWindowAttributes = new x11.XWindowAttributes;
-    let status = x11.XGetWindowAttributes(x11.current.Display, xid, xWindowAttributes.address());
-    log.debug("xWindowAttributes: "+xWindowAttributes);
-    let xEventMask = xWindowAttributes.your_event_mask;
-    let xEventMaskNeeded = x11.VisibilityChangeMask|x11.StructureNotifyMask|
-      x11.FocusChangeMask|x11.PropertyChangeMask;
-    log.debug("xEventMask="+xEventMask+" xEventMaskNeeded="+xEventMaskNeeded);
-    if ((xEventMask & xEventMaskNeeded) !== xEventMaskNeeded) {
-      log.error("missing mandatory event-masks"); // change with gdk_window_set_events()
-    }
-  },
-
-  filterWindow: function(xev, gdkEv, data) {
-    if (!xev)
-      return gdk.GDK_FILTER_CONTINUE;
-
-    let xany = ctypes.cast(xev, x11.XAnyEvent.ptr);
-    let xid = xany.contents.window;
-
-    switch (xany.contents.type) {
-
-    case x11.MapNotify:
-      log.debug("MapNotify");
-      let gdkWinStateOnMap = gdk.gdk_window_get_state(firetray.Handler.gdkWindows.get(xid));
-      log.debug("gdkWinState="+gdkWinStateOnMap+" for xid="+xid);
-      let win = firetray.Handler.windows[xid];
-      if (firetray.Handler.appStarted && !win.visible) {
-        // when app hidden at startup, then called from command line without
-        // any argument (not through FireTray that is)
-        log.warn("window not visible, correcting visibility");
-        firetray.Window.updateVisibility(xid, true);
-        log.debug("visibleWindowsCount="+firetray.Handler.visibleWindowsCount);
-      }
-      break;
-
-    case x11.UnmapNotify:       // for catching 'iconify'
-      log.debug("UnmapNotify");
-
-      let winStates = firetray.Window.getXWindowStates(xid);
-      let isHidden =  winStates & FIRETRAY_XWINDOW_HIDDEN;
-      log.debug("winStates="+winStates+", isHidden="+isHidden);
-      // NOTE: Gecko 8.0 provides the 'sizemodechange' event
-      if (isHidden) {
-        log.debug("GOT ICONIFIED");
-        let hides_on_minimize = firetray.Utils.prefService.getBoolPref('hides_on_minimize');
-        let hides_single_window = firetray.Utils.prefService.getBoolPref('hides_single_window');
-        if (hides_on_minimize) {
-          if (hides_single_window)
-            firetray.Handler.hideWindow(xid);
-          else
-            firetray.Handler.hideAllWindows();
-        }
-      }
-      break;
+  // better visual effect if visibility set after restorePosition, but some
+  // WMs like compiz seem not to honor position setting if window not visible
+  firetray.Window.setVisibility(xid, true);
 
-      // default:
-      //   log.debug("xany.type="+xany.contents.type);
-      //   break;
-    }
+  // after show
+  firetray.Window.restoreDesktop(xid);
+  if (firetray.Utils.prefService.getBoolPref('show_activates'))
+    firetray.Window.activate(xid);
 
+  firetray.PopupMenu.hideWindowItemAndSeparatorMaybe(xid);
+  firetray.Handler.showHideIcon();
+};
+
+/* FIXME: hiding windows should also hide child windows, like message windows
+ in Thunderbird */
+firetray.Window.hide = function(xid) {
+  log.debug("hide");
+
+  firetray.Window.savePositionAndSize(xid);
+  firetray.Window.saveStates(xid);
+  firetray.Window.saveDesktop(xid);
+
+  firetray.Window.setVisibility(xid, false);
+
+  firetray.PopupMenu.showWindowItem(xid);
+  firetray.Handler.showHideIcon();
+};
+
+firetray.Window.startupHide = function(xid) {
+  log.debug('startupHide: '+xid);
+
+  // also it seems cleaner, baseWin.visibility=false removes the possibility
+  // to restore the app by calling it from the command line. Not sure why...
+  firetray.Window.setVisibility(xid, false);
+
+  firetray.PopupMenu.showWindowItem(xid);
+  firetray.Handler.showHideIcon();
+};
+
+firetray.Window.savePositionAndSize = function(xid) {
+  let gx = {}, gy = {}, gwidth = {}, gheight = {};
+  firetray.Handler.windows[xid].baseWin.getPositionAndSize(gx, gy, gwidth, gheight);
+  firetray.Handler.windows[xid].savedX = gx.value;
+  firetray.Handler.windows[xid].savedY = gy.value;
+  firetray.Handler.windows[xid].savedWidth = gwidth.value;
+  firetray.Handler.windows[xid].savedHeight = gheight.value;
+  log.debug("save: gx="+gx.value+", gy="+gy.value+", gwidth="+gwidth.value+", gheight="+gheight.value);
+};
+
+firetray.Window.restorePositionAndSize = function(xid) {
+  if ("undefined" === typeof(firetray.Handler.windows[xid].savedX))
+    return; // windows[xid].saved* may not be initialized
+
+  log.debug("restore: x="+firetray.Handler.windows[xid].savedX+", y="+firetray.Handler.windows[xid].savedY+", w="+firetray.Handler.windows[xid].savedWidth+", h="+firetray.Handler.windows[xid].savedHeight);
+  firetray.Handler.windows[xid].baseWin.setPositionAndSize(
+    firetray.Handler.windows[xid].savedX,
+    firetray.Handler.windows[xid].savedY,
+    firetray.Handler.windows[xid].savedWidth,
+    firetray.Handler.windows[xid].savedHeight,
+    false); // repaint
+
+  ['savedX', 'savedX', 'savedWidth', 'savedHeight'].forEach(function(element) {
+    delete firetray.Handler.windows[xid][element];
+  });
+};
+
+firetray.Window.saveStates = function(xid) {
+  let winStates = firetray.Window.getXWindowStates(x11.Window(xid));
+  firetray.Handler.windows[xid].savedStates = winStates;
+  log.debug("save: windowStates="+winStates);
+};
+
+// NOTE: fluxbox bug probably: if hidden and restored iconified, then
+// switching to desktop de-iconifies it ?!
+firetray.Window.restoreStates = function(xid) {
+  let winStates = firetray.Handler.windows[xid].savedStates;
+  log.debug("restored WindowStates: " + winStates);
+
+  if (winStates & FIRETRAY_XWINDOW_HIDDEN) {
+    firetray.Handler.windows[xid].chromeWin.minimize();
+    log.debug("restored minimized");
+  }
+
+  /* we expect the WM to actually show the window *not* minimized once
+   restored */
+  if (firetray.Utils.prefService.getBoolPref('hides_on_minimize'))
+    // help prevent getting iconify event following show()
+    firetray.Handler.windows[xid].chromeWin.restore(); // nsIDOMChromeWindow.idl
+
+  if (winStates & FIRETRAY_XWINDOW_MAXIMIZED) {
+    firetray.Handler.windows[xid].chromeWin.maximize();
+    log.debug("restored maximized");
+  }
+
+  delete firetray.Handler.windows[xid].savedStates;
+};
+
+firetray.Window.saveDesktop = function(xid) {
+  if (!firetray.Utils.prefService.getBoolPref('remember_desktop'))
+    return;
+
+  let winDesktop = firetray.Window.getXWindowDesktop(x11.Window(xid));
+  firetray.Handler.windows[xid].savedDesktop = winDesktop;
+  log.debug("save: windowDesktop="+winDesktop);
+};
+
+firetray.Window.restoreDesktop = function(xid) {
+  if (!firetray.Utils.prefService.getBoolPref('remember_desktop'))
+    return;
+
+  let desktopDest = firetray.Handler.windows[xid].savedDesktop;
+  if (desktopDest === null || "undefined" === typeof(desktopDest)) return;
+
+  let dataSize = 1;
+  let data = ctypes.long(dataSize);
+  data[0] = desktopDest;
+  this.xSendClientMessgeEvent(xid, x11.current.Atoms._NET_WM_DESKTOP, data, dataSize);
+
+  log.debug("restored to desktop: "+desktopDest);
+  delete firetray.Handler.windows[xid].savedDesktop;
+};
+
+firetray.Window.setVisibility = function(xid, visibility) {
+  log.debug("setVisibility="+visibility);
+  let gtkWidget = ctypes.cast(firetray.Handler.gtkWindows.get(xid), gtk.GtkWidget.ptr);
+  if (visibility)
+    gtk.gtk_widget_show_all(gtkWidget);
+  else
+    gtk.gtk_widget_hide(gtkWidget);
+
+  this.updateVisibility(xid, visibility);
+};
+// firetray.Window.updateVisibility inherited
+
+firetray.Window.xSendClientMessgeEvent = function(xid, atom, data, dataSize) {
+  let xev = new x11.XClientMessageEvent;
+  xev.type = x11.ClientMessage;
+  xev.window = x11.Window(xid);
+  xev.message_type = atom;
+  xev.format = 32;
+  for (let i=0; i<dataSize; ++i)
+    xev.data[i] = data[i];
+
+  let rootWin = x11.XDefaultRootWindow(x11.current.Display);
+  let propagate = false;
+  let mask = ctypes.long(x11.SubstructureNotifyMask|x11.SubstructureRedirectMask);
+  // fortunately, it's OK not to cast xev. ctypes.cast to a void_t doesn't work (length pb)
+  let status = x11.XSendEvent(x11.current.Display, rootWin, propagate, mask, xev.address());
+  // always returns 1 (BadRequest as a coincidence)
+};
+
+/**
+ * raises window on top and give focus.
+ */
+firetray.Window.activate = function(xid) {
+  gtk.gtk_window_present(firetray.Handler.gtkWindows.get(xid));
+  log.debug("window raised");
+};
+
+firetray.Window.setUrgency = function(xid, urgent) {
+  log.debug("setUrgency: "+urgent);
+  gtk.gtk_window_set_urgency_hint(firetray.Handler.gtkWindows.get(xid), urgent);
+};
+
+/**
+ * YOU MUST x11.XFree() THE VARIABLE RETURNED BY THIS FUNCTION
+ * @param xwin: a x11.Window
+ * @param prop: a x11.Atom
+ */
+firetray.Window.getXWindowProperties = function(xwin, prop) {
+  // infos returned by XGetWindowProperty() - FIXME: should be freed ?
+  let actual_type = new x11.Atom;
+  let actual_format = new ctypes.int;
+  let nitems = new ctypes.unsigned_long;
+  let bytes_after = new ctypes.unsigned_long;
+  let prop_value = new ctypes.unsigned_char.ptr;
+
+  let bufSize = XATOMS_EWMH_WM_STATES.length*ctypes.unsigned_long.size;
+  let offset = 0;
+  let res = x11.XGetWindowProperty(
+    x11.current.Display, xwin, prop, offset, bufSize, 0, x11.AnyPropertyType,
+    actual_type.address(), actual_format.address(), nitems.address(),
+    bytes_after.address(), prop_value.address());
+  log.debug("XGetWindowProperty res="+res+", actual_type="+actual_type.value+", actual_format="+actual_format.value+", bytes_after="+bytes_after.value+", nitems="+nitems.value);
+
+  if (!firetray.js.strEquals(res, x11.Success)) {
+    log.error("XGetWindowProperty failed");
+    return [null, null];
+  }
+  if (firetray.js.strEquals(actual_type.value, x11.None)) {
+    log.debug("property not found");
+    return [null, null];
+  }
+
+  log.debug("prop_value="+prop_value+", size="+prop_value.constructor.size);
+  /* If the returned format is 32, the property data will be stored as an
+   array of longs (which in a 64-bit application will be 64-bit values
+   that are padded in the upper 4 bytes). [man XGetWindowProperty] */
+  if (actual_format.value !== 32) {
+    log.error("unsupported format: "+actual_format.value);
+  }
+  log.debug("format OK");
+  var props = ctypes.cast(prop_value, ctypes.unsigned_long.array(nitems.value).ptr);
+  log.debug("props="+props+", size="+props.constructor.size);
+
+  return [props, nitems];
+};
+
+/**
+ * check the state of a window by its EWMH window state. This is more
+ * accurate than the chromeWin.windowState or the GdkWindowState which are
+ * based on WM_STATE. For instance, WM_STATE becomes 'Iconic' on virtual
+ * desktop change...
+ */
+firetray.Window.getXWindowStates = function(xwin) {
+  let winStates = 0;
+
+  let [propsFound, nitems] =
+        firetray.Window.getXWindowProperties(xwin, x11.current.Atoms._NET_WM_STATE);
+  log.debug("propsFound, nitems="+propsFound+", "+nitems);
+  if (!propsFound) return 0;
+
+  let maximizedHorz = maximizedVert = false;
+  for (let i=0, len=nitems.value; i<len; ++i) {
+    log.debug("i: "+propsFound.contents[i]);
+    let currentProp = propsFound.contents[i];
+    if (firetray.js.strEquals(currentProp, x11.current.Atoms['_NET_WM_STATE_HIDDEN']))
+      winStates |= FIRETRAY_XWINDOW_HIDDEN;
+    else if (firetray.js.strEquals(currentProp, x11.current.Atoms['_NET_WM_STATE_MAXIMIZED_HORZ']))
+      maximizedHorz = true;
+    else if (firetray.js.strEquals(currentProp, x11.current.Atoms['_NET_WM_STATE_MAXIMIZED_VERT']))
+      maximizedVert = true;
+  }
+
+  if (maximizedHorz && maximizedVert)
+    winStates |= FIRETRAY_XWINDOW_MAXIMIZED;
+
+  x11.XFree(propsFound);
+
+  return winStates;
+};
+
+firetray.Window.getXWindowDesktop = function(xwin) {
+  let desktop = null;
+
+  let [propsFound, nitems] =
+        firetray.Window.getXWindowProperties(xwin, x11.current.Atoms._NET_WM_DESKTOP);
+  log.debug("DESKTOP propsFound, nitems="+propsFound+", "+nitems);
+  if (!propsFound) return null;
+
+  if (firetray.js.strEquals(nitems.value, 0))
+    log.warn("desktop number not found");
+  else if (firetray.js.strEquals(nitems.value, 1))
+    desktop = propsFound.contents[0];
+  else
+    throw new RangeError("more than one desktop found");
+
+  x11.XFree(propsFound);
+
+  return desktop;
+};
+
+firetray.Window.getWindowTitle = function(xid) {
+  let title = firetray.Handler.windows[xid].baseWin.title;
+  log.debug("|baseWin.title="+title+"|");
+  let tailIndex;
+  tailIndex = title.indexOf(" - Mozilla "+firetray.Handler.appName);
+  if (tailIndex === -1)
+    tailIndex = title.indexOf(" - "+firetray.Handler.appName);
+
+  if (tailIndex !== -1)
+    return title.substring(0, tailIndex);
+  else if (title === "Mozilla "+firetray.Handler.appName)
+    return title;
+  else
+    return null;
+};
+
+firetray.Window.checkSubscribedEventMasks = function(xid) {
+  let xWindowAttributes = new x11.XWindowAttributes;
+  let status = x11.XGetWindowAttributes(x11.current.Display, xid, xWindowAttributes.address());
+  log.debug("xWindowAttributes: "+xWindowAttributes);
+  let xEventMask = xWindowAttributes.your_event_mask;
+  let xEventMaskNeeded = x11.VisibilityChangeMask|x11.StructureNotifyMask|
+        x11.FocusChangeMask|x11.PropertyChangeMask;
+  log.debug("xEventMask="+xEventMask+" xEventMaskNeeded="+xEventMaskNeeded);
+  if ((xEventMask & xEventMaskNeeded) !== xEventMaskNeeded) {
+    log.error("missing mandatory event-masks"); // change with gdk_window_set_events()
+  }
+};
+
+firetray.Window.filterWindow = function(xev, gdkEv, data) {
+  if (!xev)
     return gdk.GDK_FILTER_CONTINUE;
-  },
 
-  startupFilter: function(xev, gdkEv, data) {
-    if (!xev)
-      return gdk.GDK_FILTER_CONTINUE;
+  let xany = ctypes.cast(xev, x11.XAnyEvent.ptr);
+  let xid = xany.contents.window;
 
-    let xany = ctypes.cast(xev, x11.XAnyEvent.ptr);
-    let xid = xany.contents.window;
+  switch (xany.contents.type) {
 
-    if (xany.contents.type === x11.MapNotify) {
-      gdk.gdk_window_remove_filter(firetray.Handler.gdkWindows.get(xid),
-        firetray.Handler.windows[xid].startupFilterCb, null);
-      if (firetray.Utils.prefService.getBoolPref('start_hidden')) {
-        log.debug("start_hidden");
-        firetray.Window.startupHide(xid);
+  case x11.MapNotify:
+    log.debug("MapNotify");
+    let gdkWinStateOnMap = gdk.gdk_window_get_state(firetray.Handler.gdkWindows.get(xid));
+    log.debug("gdkWinState="+gdkWinStateOnMap+" for xid="+xid);
+    let win = firetray.Handler.windows[xid];
+    if (firetray.Handler.appStarted && !win.visible) {
+      // when app hidden at startup, then called from command line without
+      // any argument (not through FireTray that is)
+      log.warn("window not visible, correcting visibility");
+      firetray.Window.updateVisibility(xid, true);
+      log.debug("visibleWindowsCount="+firetray.Handler.visibleWindowsCount);
+    }
+    break;
+
+  case x11.UnmapNotify:       // for catching 'iconify'
+    log.debug("UnmapNotify");
+
+    let winStates = firetray.Window.getXWindowStates(xid);
+    let isHidden =  winStates & FIRETRAY_XWINDOW_HIDDEN;
+    log.debug("winStates="+winStates+", isHidden="+isHidden);
+    // NOTE: Gecko 8.0 provides the 'sizemodechange' event
+    if (isHidden) {
+      log.debug("GOT ICONIFIED");
+      let hides_on_minimize = firetray.Utils.prefService.getBoolPref('hides_on_minimize');
+      let hides_single_window = firetray.Utils.prefService.getBoolPref('hides_single_window');
+      if (hides_on_minimize) {
+        if (hides_single_window)
+          firetray.Handler.hideWindow(xid);
+        else
+          firetray.Handler.hideAllWindows();
       }
     }
+    break;
+
+    // default:
+    //   log.debug("xany.type="+xany.contents.type);
+    //   break;
+  }
+
+  return gdk.GDK_FILTER_CONTINUE;
+};
 
+firetray.Window.startupFilter = function(xev, gdkEv, data) {
+  if (!xev)
     return gdk.GDK_FILTER_CONTINUE;
-  },
-
-  showAllWindowsAndActivate: function() {
-    let visibilityRate = firetray.Handler.visibleWindowsCount/firetray.Handler.windowsCount;
-    log.debug("visibilityRate="+visibilityRate);
-    if (visibilityRate < 1)
-      firetray.Handler.showAllWindows();
-
-    for(var key in firetray.Handler.windows); // FIXME: this is not the proper way for finding the last registered window !
-    firetray.Window.activate(key);
-  },
-
-  attachOnFocusInCallback: function(xid) {
-    log.debug("attachOnFocusInCallback xid="+xid);
-    this.signals['focus-in'].callback[xid] =
-      gtk.GCallbackWidgetFocusEvent_t(firetray.Window.onFocusIn);
-    this.signals['focus-in'].handler[xid] = gobject.g_signal_connect(
-      firetray.Handler.gtkWindows.get(xid), "focus-in-event",
-      firetray.Window.signals['focus-in'].callback[xid], null);
-    log.debug("focus-in handler="+this.signals['focus-in'].handler[xid]);
-  },
-
-  detachOnFocusInCallback: function(xid) {
-    log.debug("detachOnFocusInCallback xid="+xid);
-    let gtkWin = firetray.Handler.gtkWindows.get(xid);
-    gobject.g_signal_handler_disconnect(gtkWin, this.signals['focus-in'].handler[xid]);
-    delete this.signals['focus-in'].callback[xid];
-    delete this.signals['focus-in'].handler[xid];
-  },
-
-  // NOTE: fluxbox issues a FocusIn event when switching workspace
-  // by hotkey, which means 2 FocusIn events when switching to a moz app :(
-  // (http://sourceforge.net/tracker/index.php?func=detail&aid=3190205&group_id=35398&atid=413960)
-  onFocusIn: function(widget, event, data) {
-    log.debug("onFocusIn");
-    let xid = firetray.Window.getXIDFromGtkWidget(widget);
-    log.debug("xid="+xid);
-
-    firetray.Window.setUrgency(xid, false);
 
-    if (firetray.Handler.isChatEnabled() && firetray.Chat.initialized) {
-      firetray.Chat.stopGetAttentionMaybe(xid);
+  let xany = ctypes.cast(xev, x11.XAnyEvent.ptr);
+  let xid = xany.contents.window;
+
+  if (xany.contents.type === x11.MapNotify) {
+    gdk.gdk_window_remove_filter(firetray.Handler.gdkWindows.get(xid),
+                                 firetray.Handler.windows[xid].startupFilterCb, null);
+    if (firetray.Utils.prefService.getBoolPref('start_hidden')) {
+      log.debug("start_hidden");
+      firetray.Window.startupHide(xid);
     }
   }
 
-}; // firetray.Window
+  return gdk.GDK_FILTER_CONTINUE;
+};
+
+firetray.Window.showAllWindowsAndActivate = function() {
+  let visibilityRate = firetray.Handler.visibleWindowsCount/firetray.Handler.windowsCount;
+  log.debug("visibilityRate="+visibilityRate);
+  if (visibilityRate < 1)
+    firetray.Handler.showAllWindows();
+
+  for(var key in firetray.Handler.windows); // FIXME: this is not the proper way for finding the last registered window !
+  firetray.Window.activate(key);
+};
+
+firetray.Window.attachOnFocusInCallback = function(xid) {
+  log.debug("attachOnFocusInCallback xid="+xid);
+  this.signals['focus-in'].callback[xid] =
+    gtk.GCallbackWidgetFocusEvent_t(firetray.Window.onFocusIn);
+  this.signals['focus-in'].handler[xid] = gobject.g_signal_connect(
+    firetray.Handler.gtkWindows.get(xid), "focus-in-event",
+    firetray.Window.signals['focus-in'].callback[xid], null);
+  log.debug("focus-in handler="+this.signals['focus-in'].handler[xid]);
+};
+
+firetray.Window.detachOnFocusInCallback = function(xid) {
+  log.debug("detachOnFocusInCallback xid="+xid);
+  let gtkWin = firetray.Handler.gtkWindows.get(xid);
+  gobject.g_signal_handler_disconnect(gtkWin, this.signals['focus-in'].handler[xid]);
+  delete this.signals['focus-in'].callback[xid];
+  delete this.signals['focus-in'].handler[xid];
+};
+
+// NOTE: fluxbox issues a FocusIn event when switching workspace
+// by hotkey, which means 2 FocusIn events when switching to a moz app :(
+// (http://sourceforge.net/tracker/index.php?func=detail&aid=3190205&group_id=35398&atid=413960)
+firetray.Window.onFocusIn = function(widget, event, data) {
+  log.debug("onFocusIn");
+  let xid = firetray.Window.getXIDFromGtkWidget(widget);
+  log.debug("xid="+xid);
+
+  firetray.Window.setUrgency(xid, false);
+
+  if (firetray.Handler.isChatEnabled() && firetray.Chat.initialized) {
+    firetray.Chat.stopGetAttentionMaybe(xid);
+  }
+};
 
 
 ///////////////////////// firetray.Handler overriding /////////////////////////
diff --git a/src/modules/winnt/FiretrayStatusIcon.jsm b/src/modules/winnt/FiretrayStatusIcon.jsm
index 012b946..53055b5 100644
--- a/src/modules/winnt/FiretrayStatusIcon.jsm
+++ b/src/modules/winnt/FiretrayStatusIcon.jsm
@@ -1,5 +1,8 @@
 /* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 
+/* The tray icon for the main app. We need a hidden proxy window as (1) we want
+ a unique icon, (2) the icon sends notifications to a single window. */
+
 var EXPORTED_SYMBOLS = [ "firetray" ];
 
 const Cc = Components.classes;
@@ -17,6 +20,9 @@ Cu.import("resource://firetray/winnt/FiretrayWin32.jsm");
 Cu.import("resource://firetray/commons.js");
 firetray.Handler.subscribeLibsForClosing([kernel32, shell32, user32]);
 
+const kMessageTray     = "_FIRETRAY_TrayMessage";
+const kMessageCallback = "_FIRETRAY_TrayCallback";
+
 let log = firetray.Logging.getLogger("firetray.StatusIcon");
 
 if ("undefined" == typeof(firetray.Handler))
@@ -27,7 +33,8 @@ firetray.StatusIcon = {
   initialized: false,
   callbacks: {}, // pointers to JS functions. MUST LIVE DURING ALL THE EXECUTION
   notifyIconData: null,
-  hwndHidden: null,
+  msg: {WM_TASKBARCREATED:null, WM_TRAYMESSAGE:null, WM_TRAYCALLBACK:null},
+  hwndProxy: null,
   WNDCLASS_NAME: "FireTrayHiddenWindowClass",
   WNDCLASS_ATOM: null,
 
@@ -35,6 +42,7 @@ firetray.StatusIcon = {
     this.FILENAME_BLANK = firetray.Utils.chromeToPath(
       "chrome://firetray/skin/blank-icon.png");
 
+    this.registerMessages();
     this.create();
 
     this.initialized = true;
@@ -49,8 +57,19 @@ firetray.StatusIcon = {
     this.initialized = false;
   },
 
+  registerMessages: function() {
+    this.msg.WM_TASKBARCREATED = user32.RegisterWindowMessageW("TaskbarCreated");
+    this.msg.WM_TRAYMESSAGE  = user32.RegisterWindowMessageW(kMessageTray);
+    this.msg.WM_TRAYCALLBACK = user32.RegisterWindowMessageW(kMessageCallback);
+    log.debug("WM_*="+this.msg.WM_TASKBARCREATED+" "+this.msg.WM_TRAYMESSAGE+" "+this.msg.WM_TRAYCALLBACK);
+  },
+
+  unregisterMessages: function() {
+    // FIXME: TODO:
+  },
+
   create: function() {
-    let hwnd_hidden = this.createHiddenWindow();
+    let hwnd_hidden = this.createProxyWindow();
 
     // the Mozilla hidden window has the default Mozilla icon
     let hwnd_hidden_moz = user32.FindWindowW("MozillaHiddenWindowClass", null);
@@ -62,7 +81,7 @@ firetray.StatusIcon = {
     nid.szTip = firetray.Handler.appName;
     nid.hIcon = this.getIconFromWindow(hwnd_hidden_moz);
     nid.hWnd = hwnd_hidden;
-    nid.uCallbackMessage = firetray.Win32.WM_TRAYMESSAGE;
+    nid.uCallbackMessage = this.msg.WM_TRAYMESSAGE;
     nid.uFlags = shell32.NIF_ICON | shell32.NIF_MESSAGE | shell32.NIF_TIP;
     nid.uVersion = shell32.NOTIFYICON_VERSION_4;
 
@@ -73,13 +92,13 @@ firetray.StatusIcon = {
     log.debug("Shell_NotifyIcon SETVERSION="+rv+" winLastError="+ctypes.winLastError);
 
     this.notifyIconData = nid;
-    this.hwndHidden = hwnd_hidden;
+    this.hwndProxy = hwnd_hidden;
   },
 
-  createHiddenWindow: function() {
+  createProxyWindow: function() {
     this.registerWindowClass();
 
-    this.callbacks.hiddenWinProc = user32.WNDPROC(firetray.StatusIcon.hiddenWindowProc);
+    this.callbacks.hiddenWinProc = user32.WNDPROC(firetray.StatusIcon.proxyWindowProc);
 
     let hwnd_hidden = user32.CreateWindowExW(
       0, win32.LPCTSTR(this.WNDCLASS_ATOM), // lpClassName can also be _T(WNDCLASS_NAME)
@@ -107,9 +126,31 @@ firetray.StatusIcon = {
     log.debug("WNDCLASS_ATOM="+this.WNDCLASS_ATOM);
   },
 
-  hiddenWindowProc: function(hWnd, uMsg, wParam, lParam) {
-    log.debug("HiddenWindowProc CALLED: hWnd="+hWnd+", uMsg="+uMsg+", wParam="+wParam+", lParam="+lParam);
-    // ... do something smart with this event!
+  proxyWindowProc: function(hWnd, uMsg, wParam, lParam) {
+    // log.debug("ProxyWindowProc CALLED: hWnd="+hWnd+", uMsg="+uMsg+", wParam="+wParam+", lParam="+lParam);
+
+    if (uMsg === firetray.StatusIcon.msg.WM_TASKBARCREATED) {
+      log.info("____________TASKBARCREATED");
+
+    } else if (uMsg === firetray.StatusIcon.msg.WM_TRAYMESSAGE) {
+
+      switch (+lParam) {
+      case win32.WM_LBUTTONUP:
+        log.debug("WM_LBUTTONUP");
+        break;
+      case win32.WM_RBUTTONUP:
+        log.debug("WM_RBUTTONUP");
+        break;
+      case win32.WM_CONTEXTMENU:
+        log.debug("WM_CONTEXTMENU");
+        break;
+      case win32.NIN_KEYSELECT:
+        log.debug("NIN_KEYSELECT");
+        break;
+      default:
+      }
+
+    }
 
     return user32.DefWindowProcW(hWnd, uMsg, wParam, lParam);
   },
@@ -138,8 +179,8 @@ firetray.StatusIcon = {
     return icon;
   },
 
-  destroyHiddenWindow: function() {
-    let rv = user32.DestroyWindow(this.hwndHidden);
+  destroyProxyWindow: function() {
+    let rv = user32.DestroyWindow(this.hwndProxy);
 
     rv = this.unregisterWindowClass();
     log.debug("Hidden window removed");
@@ -152,7 +193,7 @@ firetray.StatusIcon = {
   destroy: function() {
     let rv = shell32.Shell_NotifyIconW(shell32.NIM_DELETE, this.notifyIconData.address());
     log.debug("Shell_NotifyIcon DELETE="+rv+" winLastError="+ctypes.winLastError);
-    this.destroyHiddenWindow();
+    this.destroyProxyWindow();
   }
 
 }; // firetray.StatusIcon
diff --git a/src/modules/winnt/FiretrayWin32.jsm b/src/modules/winnt/FiretrayWin32.jsm
index 2cfa089..07b24ab 100644
--- a/src/modules/winnt/FiretrayWin32.jsm
+++ b/src/modules/winnt/FiretrayWin32.jsm
@@ -18,16 +18,7 @@ let log = firetray.Logging.getLogger("firetray.Win32");
 if ("undefined" == typeof(firetray.Handler))
   log.error("This module MUST be imported from/after FiretrayHandler !");
 
-const kMessageTray     = "_FIRETRAY_TrayMessage";
-const kMessageCallback = "_FIRETRAY_TrayCallback";
-
 function Win32Env() {
-  this.WM_TASKBARCREATED = user32.RegisterWindowMessageW("TaskbarCreated");
-  // We register this as well, as we cannot know which WM_USER values are
-  // already taken
-  this.WM_TRAYMESSAGE  = user32.RegisterWindowMessageW(kMessageTray);
-  this.WM_TRAYCALLBACK = user32.RegisterWindowMessageW(kMessageCallback);
-  log.debug("WM_*="+this.WM_TASKBARCREATED+" "+this.WM_TRAYMESSAGE+" "+this.WM_TRAYCALLBACK);
 
   this.hInstance = kernel32.GetModuleHandleW("xul"); // ordinary windows are created from xul.dll
   log.debug("hInstance="+this.hInstance);
diff --git a/src/modules/winnt/FiretrayWindow.jsm b/src/modules/winnt/FiretrayWindow.jsm
index 6215a9b..fe712d5 100644
--- a/src/modules/winnt/FiretrayWindow.jsm
+++ b/src/modules/winnt/FiretrayWindow.jsm
@@ -13,6 +13,7 @@ Cu.import("resource://firetray/ctypes/ctypesMap.jsm");
 Cu.import("resource://firetray/ctypes/winnt/win32.jsm");
 Cu.import("resource://firetray/ctypes/winnt/user32.jsm");
 Cu.import("resource://firetray/winnt/FiretrayWin32.jsm");
+Cu.import("resource://firetray/FiretrayWindow.jsm");
 Cu.import("resource://firetray/commons.js");
 firetray.Handler.subscribeLibsForClosing([user32]);
 
@@ -32,47 +33,42 @@ XPCOMUtils.defineLazyServiceGetter(
 const FIRETRAY_XWINDOW_HIDDEN    = 1 << 0; // when minimized also
 const FIRETRAY_XWINDOW_MAXIMIZED = 1 << 1;
 
-// // NOTE: storing ctypes pointers into a JS object doesn't work: pointers are
-// // "evolving" after a while (maybe due to back and forth conversion). So we
-// // need to store them into a real ctypes array !
-// firetray.Handler.gtkWindows              = new ctypesMap(gtk.GtkWindow.ptr),
 
+firetray.Window = new FiretrayWindow();
 
-firetray.Window = {
-  signals: {'focus-in': {callback: {}, handler: {}}},
-
-  init: function() {
+firetray.Window.init = function() {
     this.initialized = true;
-  },
-
-  shutdown: function() {
-    this.initialized = false;
-  },
+  };
 
-  show: function(xid) {
-    log.debug("show xid="+xid);
-  },
+firetray.Window.shutdown = function() {
+  this.initialized = false;
+};
 
-  hide: function(xid) {
-    log.debug("hide");
-  },
+firetray.Window.show = function(xid) {
+  log.debug("show xid="+xid);
+};
 
-  startupHide: function(xid) {
-    log.debug('startupHide: '+xid);
-  },
+firetray.Window.hide = function(xid) {
+  log.debug("hide");
+};
 
-  setVisibility: function(xid, visibility) {
-  },
+firetray.Window.startupHide = function(xid) {
+  log.debug('startupHide: '+xid);
+};
 
-}; // firetray.Window
+firetray.Window.setVisibility = function(xid, visibility) {
+};
 
 
 ///////////////////////// firetray.Handler overriding /////////////////////////
 
 /** debug facility */
 firetray.Handler.dumpWindows = function() {
-  log.debug(firetray.Handler.windowsCount);
-  for (let winId in firetray.Handler.windows) log.info(winId+"="+firetray.Handler.gtkWindows.get(winId));
+  let dumpStr = ""+firetray.Handler.windowsCount;
+  for (let wid in firetray.Handler.windows) {
+    dumpStr += " 0x"+wid;
+  }
+  log.info(dumpStr);
 };
 
 firetray.Handler.getWindowIdFromChromeWindow = firetray.Window.getXIDFromChromeWindow;
@@ -85,38 +81,33 @@ firetray.Handler.registerWindow = function(win) {
   let hwnd = nativeHandle ?
         new ctypes.voidptr_t(ctypes.UInt64(nativeHandle)) :
         user32.FindWindowW("MozillaWindowClass", win.document.title);
-  log.debug("=== hwnd="+hwnd);
+  // wid will be used as a string most of the time (through f.Handler.windows mainly)
+  let wid = ctypes.cast(hwnd, ctypes.uintptr_t).value.toString(16);
+  log.debug("=== hwnd="+hwnd+" wid="+wid+" win.document.title: "+win.document.title);
+
+  if (this.windows.hasOwnProperty(wid)) {
+    let msg = "Window ("+wid+") already registered.";
+    log.error(msg);
+    Cu.reportError(msg);
+    return false;
+  }
+  this.windows[wid] = {};
+  this.windows[wid].chromeWin = win;
+  this.windows[wid].baseWin = baseWin;
 
 //   SetupWnd(hwnd);
 //   ::SetPropW(hwnd, kIconData, reinterpret_cast<HANDLE>(iconData));
 //   ::SetPropW(hwnd, kIconMouseEventProc, reinterpret_cast<HANDLE>(callback));
 //   ::SetPropW(hwnd, kIcon, reinterpret_cast<HANDLE>(0x1));
 
-  return;
-
-  // register
-  let [whndbaseWin, gtkWin, gdkWin, xid] = firetray.Window.getWindowsFromChromeWindow(win);
-  this.windows[xid] = {};
-  this.windows[xid].chromeWin = win;
-  this.windows[xid].baseWin = baseWin;
-  firetray.Window.checkSubscribedEventMasks(xid);
-  try {
-    this.gtkWindows.insert(xid, gtkWin);
-    this.gdkWindows.insert(xid, gdkWin);
-    firetray.PopupMenu.addWindowItem(xid);
-  } catch (x) {
-    if (x.name === "RangeError") // instanceof not working :-(
-      win.alert(x+"\n\nYou seem to have more than "+FIRETRAY_WINDOW_COUNT_MAX
-                +" windows open. This breaks FireTray and most probably "
-                +firetray.Handler.appName+".");
-  }
   this.windowsCount += 1;
   // NOTE: no need to check for window state to set visibility because all
   // windows *are* shown at startup
-  firetray.Window.updateVisibility(xid, true);
-  log.debug("window "+xid+" registered");
+  firetray.Window.updateVisibility(wid, true);
+  log.debug("window 0x"+wid+" registered");
   // NOTE: shouldn't be necessary to gtk_widget_add_events(gtkWin, gdk.GDK_ALL_EVENTS_MASK);
 
+/*
   try {
      // NOTE: we could try to catch the "delete-event" here and block
      // delete_event_cb (in gtk2/nsWindow.cpp), but we prefer to use the
@@ -139,9 +130,10 @@ firetray.Handler.registerWindow = function(win) {
     log.error(x);
     return null;
   }
+*/
 
   log.debug("AFTER"); firetray.Handler.dumpWindows();
-  return xid;
+  return wid;
 };
 
 firetray.Handler.unregisterWindow = function(win) {

-- 
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