[Pkg-mozext-commits] [firetray] 43/84: Add basic popup menu on tray icon.

David Prévot taffit at moszumanska.debian.org
Sun Jul 20 01:42:44 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 8a3de9e34318565dd167939d26bf5b147d53c648
Author: foudfou <foudil.newbie+git at gmail.com>
Date:   Mon Mar 31 23:17:03 2014 +0200

    Add basic popup menu on tray icon.
---
 src/chrome/skin/winnt/application-exit.bmp | Bin 0 -> 2358 bytes
 src/chrome/skin/winnt/document-new.bmp     | Bin 0 -> 2358 bytes
 src/chrome/skin/winnt/gtk-apply.bmp        | Bin 0 -> 1654 bytes
 src/chrome/skin/winnt/gtk-edit.bmp         | Bin 0 -> 2358 bytes
 src/chrome/skin/winnt/gtk-preferences.bmp  | Bin 0 -> 2358 bytes
 src/modules/ctypes/winnt/user32.jsm        |  93 ++++++++++++++++++++++
 src/modules/ctypes/winnt/win32.jsm         |  22 ++++++
 src/modules/winnt/FiretrayPopupMenu.jsm    | 121 +++++++++++++++++++++++++++++
 src/modules/winnt/FiretrayStatusIcon.jsm   |  53 ++++++++++---
 9 files changed, 278 insertions(+), 11 deletions(-)

diff --git a/src/chrome/skin/winnt/application-exit.bmp b/src/chrome/skin/winnt/application-exit.bmp
new file mode 100644
index 0000000..99e6f29
Binary files /dev/null and b/src/chrome/skin/winnt/application-exit.bmp differ
diff --git a/src/chrome/skin/winnt/document-new.bmp b/src/chrome/skin/winnt/document-new.bmp
new file mode 100644
index 0000000..e7cf0af
Binary files /dev/null and b/src/chrome/skin/winnt/document-new.bmp differ
diff --git a/src/chrome/skin/winnt/gtk-apply.bmp b/src/chrome/skin/winnt/gtk-apply.bmp
new file mode 100644
index 0000000..7b5681a
Binary files /dev/null and b/src/chrome/skin/winnt/gtk-apply.bmp differ
diff --git a/src/chrome/skin/winnt/gtk-edit.bmp b/src/chrome/skin/winnt/gtk-edit.bmp
new file mode 100644
index 0000000..38b0a87
Binary files /dev/null and b/src/chrome/skin/winnt/gtk-edit.bmp differ
diff --git a/src/chrome/skin/winnt/gtk-preferences.bmp b/src/chrome/skin/winnt/gtk-preferences.bmp
new file mode 100644
index 0000000..4d579e6
Binary files /dev/null and b/src/chrome/skin/winnt/gtk-preferences.bmp differ
diff --git a/src/modules/ctypes/winnt/user32.jsm b/src/modules/ctypes/winnt/user32.jsm
index 96680c8..c1fb5a8 100644
--- a/src/modules/ctypes/winnt/user32.jsm
+++ b/src/modules/ctypes/winnt/user32.jsm
@@ -266,6 +266,99 @@ function user32_defines(lib) {
   this.DT_NOPREFIX        = 0x00000800;
   this.DT_INTERNAL        = 0x00001000;
 
+  lib.lazy_bind("CreatePopupMenu", win32.HMENU);
+  lib.lazy_bind("DestroyMenu", win32.BOOL, win32.HMENU);
+
+  this.MENUITEMINFOW = ctypes.StructType("MENUITEMINFOW", [
+    { "cbSize": win32.UINT },
+    { "fMask": win32.UINT },
+    { "fType": win32.UINT },
+    { "fState": win32.UINT },
+    { "wID": win32.UINT },
+    { "hSubMenu": win32.HMENU },
+    { "hbmpChecked": win32.HBITMAP },
+    { "hbmpUnchecked": win32.HBITMAP },
+    { "dwItemData": win32.ULONG_PTR },
+    { "dwTypeData": win32.LPWSTR },
+    { "cch": win32.UINT },
+    { "hbmpItem": win32.HBITMAP }
+  ]);
+  this.LPCMENUITEMINFO = this.LPMENUITEMINFOW = this.MENUITEMINFOW.ptr;
+
+  lib.lazy_bind("InsertMenuItemW", win32.BOOL, win32.HMENU, win32.UINT, win32.BOOL, this.LPCMENUITEMINFO);
+  lib.lazy_bind("GetMenuItemInfoW", win32.BOOL, win32.HMENU, win32.UINT, win32.BOOL, this.LPCMENUITEMINFO);
+
+  this.MIIM_STATE      = 0x00000001;
+  this.MIIM_ID         = 0x00000002;
+  this.MIIM_SUBMENU    = 0x00000004;
+  this.MIIM_CHECKMARKS = 0x00000008;
+  this.MIIM_TYPE       = 0x00000010;
+  this.MIIM_DATA       = 0x00000020;
+  this.MIIM_STRING     = 0x00000040;
+  this.MIIM_BITMAP     = 0x00000080;
+  this.MIIM_FTYPE      = 0x00000100;
+
+  lib.lazy_bind("InsertMenuW", win32.BOOL, win32.HMENU, win32.UINT, win32.UINT, win32.UINT_PTR, win32.LPCTSTR);
+
+  this.MF_INSERT          = 0x00000000;
+  this.MF_CHANGE          = 0x00000080;
+  this.MF_APPEND          = 0x00000100;
+  this.MF_DELETE          = 0x00000200;
+  this.MF_REMOVE          = 0x00001000;
+  this.MF_BYCOMMAND       = 0x00000000;
+  this.MF_BYPOSITION      = 0x00000400;
+  this.MF_SEPARATOR       = 0x00000800;
+  this.MF_ENABLED         = 0x00000000;
+  this.MF_GRAYED          = 0x00000001;
+  this.MF_DISABLED        = 0x00000002;
+  this.MF_UNCHECKED       = 0x00000000;
+  this.MF_CHECKED         = 0x00000008;
+  this.MF_USECHECKBITMAPS = 0x00000200;
+  this.MF_STRING          = 0x00000000;
+  this.MF_BITMAP          = 0x00000004;
+  this.MF_OWNERDRAW       = 0x00000100;
+  this.MF_POPUP           = 0x00000010;
+  this.MF_MENUBARBREAK    = 0x00000020;
+  this.MF_MENUBREAK       = 0x00000040;
+  this.MF_UNHILITE        = 0x00000000;
+  this.MF_HILITE          = 0x00000080;
+  this.MF_DEFAULT         = 0x00001000;
+  this.MF_RIGHTJUSTIFY    = 0x00004000;
+  this.MFT_STRING         = this.MF_STRING;
+  this.MFT_BITMAP         = this.MF_BITMAP;
+  this.MFT_MENUBARBREAK   = this.MF_MENUBARBREAK;
+  this.MFT_MENUBREAK      = this.MF_MENUBREAK;
+  this.MFT_OWNERDRAW      = this.MF_OWNERDRAW;
+  this.MFT_RADIOCHECK     = 0x00000200;
+  this.MFT_SEPARATOR      = this.MF_SEPARATOR;
+  this.MFT_RIGHTORDER     = 0x00002000;
+  this.MFT_RIGHTJUSTIFY   = this.MF_RIGHTJUSTIFY;
+  this.MFS_GRAYED         = 0x00000003;
+  this.MFS_DISABLED       = this.MFS_GRAYED;
+  this.MFS_CHECKED        = this.MF_CHECKED;
+  this.MFS_HILITE         = this.MF_HILITE;
+  this.MFS_ENABLED        = this.MF_ENABLED;
+  this.MFS_UNCHECKED      = this.MF_UNCHECKED;
+  this.MFS_UNHILITE       = this.MF_UNHILITE;
+  this.MFS_DEFAULT        = this.MF_DEFAULT;
+
+  this.TPM_LEFTBUTTON   = 0x0000;
+  this.TPM_RIGHTBUTTON  = 0x0002;
+  this.TPM_LEFTALIGN    = 0x0000;
+  this.TPM_CENTERALIGN  = 0x0004;
+  this.TPM_RIGHTALIGN   = 0x0008;
+  this.TPM_TOPALIGN     = 0x0000;
+  this.TPM_VCENTERALIGN = 0x0010;
+  this.TPM_BOTTOMALIGN  = 0x0020;
+  this.TPM_HORIZONTAL   = 0x0000;
+  this.TPM_VERTICAL     = 0x0040;
+
+  lib.lazy_bind("CalculatePopupWindowPosition", win32.BOOL, win32.POINT.ptr, win32.SIZE, win32.UINT, win32.RECT.ptr, win32.RECT.ptr);
+  lib.lazy_bind("TrackPopupMenu", win32.BOOL, win32.HMENU, win32.UINT, ctypes.int, ctypes.int, ctypes.int, win32.HWND, win32.RECT.ptr);
+  lib.lazy_bind("SetForegroundWindow", win32.BOOL, win32.HWND);
+  lib.lazy_bind("GetCursorPos", win32.BOOL, win32.LPPOINT);
+  lib.lazy_bind("GetMessagePos", win32.DWORD);
+
 }
 
 new ctypes_library(USER32_LIBNAME, USER32_ABIS, user32_defines, this);
diff --git a/src/modules/ctypes/winnt/win32.jsm b/src/modules/ctypes/winnt/win32.jsm
index a369106..00be95f 100644
--- a/src/modules/ctypes/winnt/win32.jsm
+++ b/src/modules/ctypes/winnt/win32.jsm
@@ -33,6 +33,7 @@ var win32 = new function() {
   this.LONG_PTR  = is64bit ? ctypes.int64_t  : ctypes.long;
   this.ULONG_PTR = is64bit ? ctypes.uint64_t : ctypes.unsigned_long;
   this.SIZE_T    = this.ULONG_PTR;
+  this.DWORD_PTR = this.ULONG_PTR;
   this.ATOM      = this.WORD;
   this.HANDLE    = ctypes.voidptr_t;
   this.HWND      = this.HANDLE;
@@ -77,6 +78,21 @@ var win32 = new function() {
     return ctypes.jschar.array()(str);
   };
 
+  /*
+   * #define LOWORD(l) ((WORD)((DWORD_PTR)(l) & 0xffff))
+   * #define HIWORD(l) ((WORD)((DWORD_PTR)(l) >> 16))
+   * #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
+   * #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))
+   */
+  this.LOWORD = function(l) {return l & 0x0000ffff;};
+  this.HIWORD = function(l) {return l >> 16;};
+  /* Although we shouldn't use LO-/HIWORD to get coords, because of negative
+   coords on multi-monitor displays, I'm not sure how to express the
+   GET_?_LPARAM macros with ctypes. */
+  this.GET_X_LPARAM = this.LOWORD;
+  this.GET_Y_LPARAM = this.HIWORD;
+
+  this.ERROR_INVALID_PARAMETER       = 87;
   this.ERROR_INVALID_WINDOW_HANDLE   = 1400;
   this.ERROR_RESOURCE_TYPE_NOT_FOUND = 1813;
 
@@ -156,6 +172,12 @@ var win32 = new function() {
   ]);
   this.PICONINFO = this.ICONINFO.ptr;
 
+  this.POINT = ctypes.StructType("POINT", [
+   { "x": this.LONG },
+   { "y": this.LONG }
+  ]);
+  this.PPOINT = this.LPPOINT =this.POINT.ptr;
+
   this.RECT = ctypes.StructType("RECT", [
     { "left": this.LONG },
     { "top": this.LONG },
diff --git a/src/modules/winnt/FiretrayPopupMenu.jsm b/src/modules/winnt/FiretrayPopupMenu.jsm
new file mode 100644
index 0000000..f327779
--- /dev/null
+++ b/src/modules/winnt/FiretrayPopupMenu.jsm
@@ -0,0 +1,121 @@
+/* -*- 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/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/ctypes.jsm");
+Cu.import("resource://firetray/ctypes/winnt/win32.jsm");
+Cu.import("resource://firetray/ctypes/winnt/user32.jsm");
+Cu.import("resource://firetray/commons.js");
+firetray.Handler.subscribeLibsForClosing([user32]);
+
+let log = firetray.Logging.getLogger("firetray.PopupMenu");
+
+if ("undefined" == typeof(firetray.StatusIcon))
+  log.error("This module MUST be imported from/after StatusIcon !");
+
+// popupmenu items
+const IDM_PREF    = 100;
+const IDM_QUIT    = 200;
+const IDM_NEW_MSG = 300;
+const IDM_NEW_WND = 400;
+const IDM_RESET   = 500;
+
+
+firetray.PopupMenu = {
+  initialized: false,
+  menu: null,
+
+  init: function() {
+    this.create();
+
+    this.initialized = true;
+    return true;
+  },
+
+  shutdown: function() {
+    this.destroy();
+
+    log.debug("Disabling PopupMenu");
+    this.initialized = false;
+  },
+
+  create: function() {
+    this.menu = user32.CreatePopupMenu(); // FIXME: destroy
+    log.debug("menu="+this.menu);
+
+    var addMenuSeparator = false;
+
+    this.insertMenuItem('Quit', 'quit', IDM_QUIT);
+    user32.InsertMenuW(this.menu, 0, user32.MF_BYPOSITION|user32.MF_SEPARATOR, 0, null);
+    this.insertMenuItem('Preferences', 'prefs', IDM_PREF);
+
+    if (firetray.Handler.inBrowserApp) {
+      this.insertMenuItem('NewWindow', 'new-wnd', IDM_NEW_WND);
+      addMenuSeparator = true;
+    }
+
+    if (firetray.Handler.inMailApp) {
+      this.insertMenuItem('NewMessage', 'new-msg', IDM_NEW_MSG);
+      this.insertMenuItem('ResetIcon', 'reset', IDM_RESET);
+      addMenuSeparator = true;
+    }
+
+    if (addMenuSeparator) {
+      user32.InsertMenuW(this.menu, 2, user32.MF_BYPOSITION|user32.MF_SEPARATOR, 0, null);
+    }
+
+    // // We'll user InsertMenuW for hidden windows:
+    // user32.InsertMenuW(this.menu, 0, user32.MF_BYPOSITION|user32.MF_STRING, IDM_CLOSE, "Close"); // FIXME: ampersand doesn't work ?
+
+    log.debug("PopupMenu created");
+  },
+
+  destroy: function() {
+    user32.DestroyMenu(this.menu);
+    log.debug("PopupMenu destroyed");
+  },
+
+  insertMenuItem: function(itemName, iconName, actionId) {
+    var menuItemLabel = firetray.Utils.strings.GetStringFromName("popupMenu.itemLabel."+itemName);
+    let mii = new user32.MENUITEMINFOW();
+    // BUG: ctypes doesn't detect wrong field assignments mii.size = ... ('size' undefined)
+    mii.cbSize = user32.MENUITEMINFOW.size;
+    mii.fMask = user32.MIIM_ID | user32.MIIM_STRING | user32.MIIM_DATA;
+    mii.wID = actionId;
+    // mii.dwItemData = win32.ULONG_PTR(actionId);
+    mii.dwTypeData = win32._T(menuItemLabel);
+    /* Under XP, putting a bitmap into hbmpItem results in ugly icons. We
+     should probably use HBMMENU_CALLBACK as explained in
+     http://www.nanoant.com/programming/themed-menus-icons-a-complete-vista-xp-solution.
+     But for now, we just don't display icons in XP-. */
+    if (win32.WINVER >= win32.WIN_VERSIONS["Vista"]) {
+      mii.fMask |= user32.MIIM_BITMAP;
+      mii.hbmpItem = firetray.StatusIcon.bitmaps.get(iconName);
+    }
+    log.debug("mii="+mii);
+    if (!user32.InsertMenuItemW(this.menu, 0, true, mii.address())) {
+      log.error("InsertMenuItemW failed winLastError="+ctypes.winLastError);
+    }
+  },
+
+  processMenuItem: function(itemId) {
+    switch (itemId) {
+    case IDM_PREF: firetray.Handler.openPrefWindow(); break;
+    case IDM_QUIT: firetray.Handler.quitApplication(); break;
+    case IDM_NEW_MSG: firetray.Handler.openMailMessage(); break;
+    case IDM_NEW_WND: firetray.Handler.openBrowserWindow(); break;
+    case IDM_RESET: firetray.Handler.setIconImageDefault(); break;
+    default:
+      log.error("no action for itemId ("+itemId+")");
+    }
+  }
+
+}; // firetray.PopupMenu
+
+firetray.Handler.showHidePopupMenuItems = firetray.PopupMenu.showHideWindowItems;
diff --git a/src/modules/winnt/FiretrayStatusIcon.jsm b/src/modules/winnt/FiretrayStatusIcon.jsm
index 3b79fa7..22397fa 100644
--- a/src/modules/winnt/FiretrayStatusIcon.jsm
+++ b/src/modules/winnt/FiretrayStatusIcon.jsm
@@ -27,18 +27,23 @@ let log = firetray.Logging.getLogger("firetray.StatusIcon");
 if ("undefined" == typeof(firetray.Handler))
   log.error("This module MUST be imported from/after FiretrayHandler !");
 
-FIRETRAY_ICON_CHROME_PATHS = {
+const ICON_CHROME_PATHS = {
   'blank-icon': "chrome://firetray/skin/winnt/blank-icon.bmp",
-  'mail-unread': "chrome://firetray/skin/winnt/mail-unread.ico"
+  'mail-unread': "chrome://firetray/skin/winnt/mail-unread.ico",
+  // these are for the popup menu:
+  'prefs': "chrome://firetray/skin/winnt/gtk-preferences.bmp",
+  'quit': "chrome://firetray/skin/winnt/application-exit.bmp",
+  'new-wnd': "chrome://firetray/skin/winnt/document-new.bmp",
+  'new-msg': "chrome://firetray/skin/winnt/gtk-edit.bmp",
+  'reset': "chrome://firetray/skin/winnt/gtk-apply.bmp"
 };
 
+
 firetray.StatusIcon = {
   initialized: false,
   callbacks: {}, // pointers to JS functions. MUST LIVE DURING ALL THE EXECUTION
   notifyIconData: null,
   hwndProxy: null,
-  icons: null,
-  bitmaps: null,
   WNDCLASS_NAME: "FireTrayHiddenWindowClass",
   WNDCLASS_ATOM: null,
   icons: (function(){return new ctypesMap(win32.HICON);})(),
@@ -57,12 +62,17 @@ firetray.StatusIcon = {
     this.create();
     firetray.Handler.setIconImageDefault();
 
+    Cu.import("resource://firetray/winnt/FiretrayPopupMenu.jsm");
+    if (!firetray.PopupMenu.init())
+      return false;
+
     this.initialized = true;
     return true;
   },
 
   shutdown: function() {
     log.debug("Disabling StatusIcon");
+    firetray.PopupMenu.shutdown();
 
     this.destroy();
     this.destroyImages();
@@ -84,8 +94,8 @@ firetray.StatusIcon = {
 
     /* we'll take the first icon in the .ico file. To get the icon count in the
      file, pass ctypes.cast(ctypes.int(-1), win32.UINT); */
-    for (let imgName in FIRETRAY_ICON_CHROME_PATHS) {
-      let path = firetray.Utils.chromeToPath(FIRETRAY_ICON_CHROME_PATHS[imgName]);
+    for (let imgName in ICON_CHROME_PATHS) {
+      let path = firetray.Utils.chromeToPath(ICON_CHROME_PATHS[imgName]);
       let img = this.loadImageFromFile(path);
       if (img)
         this[this.IMG_TYPES[img['type']]['map']].insert(imgName, img['himg']);
@@ -225,23 +235,44 @@ firetray.StatusIcon = {
 
     } else if (uMsg === firetray.Win32.WM_TRAYMESSAGE) {
 
-      switch (+lParam) {
+      switch (win32.LOWORD(lParam)) {
       case win32.WM_LBUTTONUP:
         log.debug("WM_LBUTTONUP");
         firetray.Handler.showHideAllWindows();
         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");
+        /* Can't determine tray icon position precisely: the mouse cursor can
+         move between WM_RBUTTONDOWN and WM_RBUTTONUP, or the icon can have
+         been moved inside the notification area... so we opt for the easy
+         solution. */
+        let pos = user32.GetMessagePos();
+        let xPos = win32.GET_X_LPARAM(pos), yPos = win32.GET_Y_LPARAM(pos);
+        log.debug("  x="+xPos+" y="+yPos);
+        user32.SetForegroundWindow(hWnd);
+        user32.TrackPopupMenu(firetray.PopupMenu.menu, user32.TPM_RIGHTALIGN|user32.TPM_BOTTOMALIGN, xPos, yPos, 0, hWnd, null);
         break;
       default:
       }
 
+    } else {
+      switch (uMsg) {
+      case win32.WM_SYSCOMMAND:
+        log.debug("WM_SYSCOMMAND wParam="+wParam+", lParam="+lParam);
+        break;
+      case win32.WM_COMMAND:
+        log.debug("WM_COMMAND wParam="+wParam+", lParam="+lParam);
+        firetray.PopupMenu.processMenuItem(wParam);
+        break;
+      case win32.WM_MENUCOMMAND:
+        log.debug("WM_MENUCOMMAND wParam="+wParam+", lParam="+lParam);
+        break;
+      case win32.WM_MENUCHAR:
+        log.debug("WM_MENUCHAR wParam="+wParam+", lParam="+lParam);
+        break;
+      }
     }
 
     return user32.DefWindowProcW(hWnd, uMsg, wParam, lParam);

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