Add basic popup menu on tray icon.

This commit is contained in:
foudfou 2014-03-31 23:17:03 +02:00
parent 2480ffbf85
commit 8a3de9e343
9 changed files with 278 additions and 11 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -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);

View File

@ -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 },

View File

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

View File

@ -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);