mirror of
https://github.com/moparisthebest/FireTray
synced 2025-01-07 19:48:03 -05:00
Add basic popup menu on tray icon.
This commit is contained in:
parent
2480ffbf85
commit
8a3de9e343
BIN
src/chrome/skin/winnt/application-exit.bmp
Normal file
BIN
src/chrome/skin/winnt/application-exit.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
BIN
src/chrome/skin/winnt/document-new.bmp
Normal file
BIN
src/chrome/skin/winnt/document-new.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
BIN
src/chrome/skin/winnt/gtk-apply.bmp
Normal file
BIN
src/chrome/skin/winnt/gtk-apply.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
BIN
src/chrome/skin/winnt/gtk-edit.bmp
Normal file
BIN
src/chrome/skin/winnt/gtk-edit.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
BIN
src/chrome/skin/winnt/gtk-preferences.bmp
Normal file
BIN
src/chrome/skin/winnt/gtk-preferences.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.3 KiB |
@ -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);
|
||||
|
@ -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 },
|
||||
|
121
src/modules/winnt/FiretrayPopupMenu.jsm
Normal file
121
src/modules/winnt/FiretrayPopupMenu.jsm
Normal 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;
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user