diff --git a/src/modules/FiretrayHandler.jsm b/src/modules/FiretrayHandler.jsm index 24f6a2f..0b9e599 100644 --- a/src/modules/FiretrayHandler.jsm +++ b/src/modules/FiretrayHandler.jsm @@ -123,7 +123,7 @@ firetray.Handler = { firetray.Chat.init(); } else { let platforms = FIRETRAY_CHAT_SUPPORTED_OS.join(", "); - log.error("Only "+platforms+" platform(s) supported at this time. Firetray not loaded"); + log.error("Only "+platforms+" platform(s) supported at this time. Chat not loaded"); } } @@ -516,6 +516,9 @@ firetray.PrefListener = new PrefListener( firetray.Handler.setIconImageDefault(); } break; + case 'mail_notification_type': + firetray.Messaging.updateIcon(); + break; case 'new_mail_icon_names': firetray.StatusIcon.loadThemedIcons(); case 'only_favorite_folders': diff --git a/src/modules/commons.js b/src/modules/commons.js index 2f5cfcb..f03c567 100644 --- a/src/modules/commons.js +++ b/src/modules/commons.js @@ -309,7 +309,10 @@ firetray.js = { result = result.concat(Object.getOwnPropertyNames(objectToInspect)); } return result; - } + }, + + floatToInt: function(nb) { return nb >> 0; } // bitwise ops on signed int + }; // http://stackoverflow.com/questions/18912/how-to-find-keys-of-a-hash diff --git a/src/modules/ctypes/winnt/gdi32.jsm b/src/modules/ctypes/winnt/gdi32.jsm index 82b8fd0..72ea262 100644 --- a/src/modules/ctypes/winnt/gdi32.jsm +++ b/src/modules/ctypes/winnt/gdi32.jsm @@ -24,7 +24,42 @@ function gdi32_defines(lib) { lib.lazy_bind("CreateCompatibleDC", win32.HDC, win32.HDC); lib.lazy_bind("DeleteDC", win32.BOOL, win32.HDC); + lib.lazy_bind("BitBlt", win32.BOOL, win32.HDC, ctypes.int, ctypes.int, ctypes.int, ctypes.int, win32.HDC, ctypes.int, ctypes.int, win32.DWORD); + this.SRCCOPY = win32.DWORD(0x00CC0020); /* dest = source */ + this.SRCPAINT = win32.DWORD(0x00EE0086); /* dest = source OR dest */ + this.SRCAND = win32.DWORD(0x008800C6); /* dest = source AND dest */ + this.SRCINVERT = win32.DWORD(0x00660046); /* dest = source XOR dest */ + this.SRCERASE = win32.DWORD(0x00440328); /* dest = source AND (NOT dest ) */ + this.NOTSRCCOPY = win32.DWORD(0x00330008); /* dest = (NOT source) */ + this.NOTSRCERASE = win32.DWORD(0x001100A6); /* dest = (NOT src) AND (NOT dest) */ + this.MERGECOPY = win32.DWORD(0x00C000CA); /* dest = (source AND pattern) */ + this.MERGEPAINT = win32.DWORD(0x00BB0226); /* dest = (NOT source) OR dest */ + this.PATCOPY = win32.DWORD(0x00F00021); /* dest = pattern */ + this.PATPAINT = win32.DWORD(0x00FB0A09); /* dest = DPSnoo */ + this.PATINVERT = win32.DWORD(0x005A0049); /* dest = pattern XOR dest */ + this.DSTINVERT = win32.DWORD(0x00550009); /* dest = (NOT dest) */ + this.BLACKNESS = win32.DWORD(0x00000042); /* dest = BLACK */ + this.WHITENESS = win32.DWORD(0x00FF0062); /* dest = WHITE */ + this.NOMIRRORBITMAP = win32.DWORD(0x80000000); /* Do not Mirror the bitmap in this call */ + this.CAPTUREBLT = win32.DWORD(0x40000000); /* Include layered windows */ lib.lazy_bind("CreateCompatibleBitmap", win32.HBITMAP, win32.HDC, ctypes.int, ctypes.int); + lib.lazy_bind("CreateBitmapIndirect", win32.HBITMAP, win32.BITMAP.ptr); + lib.lazy_bind("GetObjectW", ctypes.int, win32.HGDIOBJ, ctypes.int, win32.LPVOID); + lib.lazy_bind("GetCurrentObject", win32.HGDIOBJ, win32.HDC, win32.UINT); + this.OBJ_PEN = 1; + this.OBJ_BRUSH = 2; + this.OBJ_DC = 3; + this.OBJ_METADC = 4; + this.OBJ_PAL = 5; + this.OBJ_FONT = 6; + this.OBJ_BITMAP = 7; + this.OBJ_REGION = 8; + this.OBJ_METAFILE = 9; + this.OBJ_MEMDC = 10; + this.OBJ_EXTPEN = 11; + this.OBJ_ENHMETADC = 12; + this.OBJ_ENHMETAFILE = 13; + this.OBJ_COLORSPACE = 14; lib.lazy_bind("SelectObject", win32.HGDIOBJ, win32.HDC, win32.HGDIOBJ); lib.lazy_bind("DeleteObject", win32.BOOL, win32.HGDIOBJ); lib.lazy_bind("PatBlt", win32.BOOL, win32.HDC, ctypes.int, ctypes.int, ctypes.int, ctypes.int, win32.DWORD); @@ -77,6 +112,13 @@ function gdi32_defines(lib) { lib.lazy_bind("TextOutW", win32.BOOL, win32.HDC, ctypes.int, ctypes.int, win32.LPCTSTR, ctypes.int); + this.SIZE = ctypes.StructType("SIZE", [ + { "cx": win32.LONG }, + { "cy": win32.LONG } + ]); + this.LPSIZE = this.SIZE.ptr; + lib.lazy_bind("GetTextExtentPoint32W", win32.BOOL, win32.HDC, win32.LPCTSTR, ctypes.int, this.LPSIZE); + lib.lazy_bind("GetTextAlign", win32.UINT, win32.HDC); lib.lazy_bind("SetTextAlign", win32.UINT, win32.HDC, win32.UINT); this.TA_LEFT = 0; @@ -88,6 +130,33 @@ function gdi32_defines(lib) { this.TA_RTLREADING = 256; this.TA_MASK =(this.TA_BASELINE+this.TA_CENTER+this.TA_UPDATECP+this.TA_RTLREADING); + this.BITMAPINFOHEADER = ctypes.StructType("BITMAPINFOHEADER", [ + { "biSize": win32.DWORD }, + { "biWidth": win32.LONG }, + { "biHeight": win32.LONG }, + { "biPlanes": win32.WORD }, + { "biBitCount": win32.WORD }, + { "biCompression": win32.DWORD }, + { "biSizeImage": win32.DWORD }, + { "biXPelsPerMeter": win32.LONG }, + { "biYPelsPerMeter": win32.LONG }, + { "biClrUsed": win32.DWORD }, + { "biClrImportant": win32.DWORD } + ]); + this.PBITMAPINFOHEADER = this.BITMAPINFOHEADER.ptr; + this.RGBQUAD = ctypes.StructType("RGBQUAD", [ + { "rgbBlue": win32.BYTE }, + { "rgbGreen": win32.BYTE }, + { "rgbRed": win32.BYTE }, + { "rgbReserved": win32.BYTE } + ]); + this.BITMAPINFO = ctypes.StructType("BITMAPINFO", [ + { "bmiHeader": this.BITMAPINFOHEADER }, + { "bmiColors": this.RGBQUAD.array(1) } + ]); + this.PBITMAPINFO = this.BITMAPINFO.ptr; + lib.lazy_bind("SetDIBits", ctypes.int, win32.HDC, win32.HBITMAP, win32.UINT, win32.UINT, ctypes.voidptr_t, this.BITMAPINFO.ptr, win32.UINT); + } new ctypes_library(GDI32_LIBNAME, GDI32_ABIS, gdi32_defines, this); diff --git a/src/modules/ctypes/winnt/user32.jsm b/src/modules/ctypes/winnt/user32.jsm index 6a0702e..96680c8 100644 --- a/src/modules/ctypes/winnt/user32.jsm +++ b/src/modules/ctypes/winnt/user32.jsm @@ -50,6 +50,7 @@ function user32_defines(lib) { this.IDI_QUESTION = win32.MAKEINTRESOURCE(32514); this.IDI_EXCLAMATION = win32.MAKEINTRESOURCE(32515); this.IDI_ASTERISK = win32.MAKEINTRESOURCE(32516); + lib.lazy_bind("DestroyIcon", win32.BOOL, win32.HICON); lib.lazy_bind("LoadImageW", win32.HANDLE, win32.HINSTANCE, win32.LPCTSTR, win32.UINT, ctypes.int, ctypes.int, win32.UINT); this.IMAGE_BITMAP = 0; this.IMAGE_ICON = 1; @@ -63,7 +64,7 @@ function user32_defines(lib) { this.LR_MONOCHROME = 0x00000001; this.LR_SHARED = 0x00008000; this.LR_VGACOLOR = 0x00000080; - lib.lazy_bind("DestroyIcon", win32.BOOL, win32.HICON); + lib.lazy_bind("CopyImage", win32.HANDLE, win32.HANDLE, win32.UINT, ctypes.int, ctypes.int, win32.UINT); lib.lazy_bind("GetPropW", win32.HANDLE, win32.HWND, win32.LPCTSTR); lib.lazy_bind("SetPropW", win32.BOOL, win32.HWND, win32.LPCTSTR, win32.HANDLE); @@ -247,6 +248,23 @@ function user32_defines(lib) { lib.lazy_bind("GetDC", win32.HDC, win32.HWND); lib.lazy_bind("ReleaseDC", ctypes.int, win32.HWND, win32.HDC); lib.lazy_bind("CreateIconIndirect", win32.HICON, win32.PICONINFO); + lib.lazy_bind("GetClientRect", win32.BOOL, win32.HWND, win32.PRECT); + lib.lazy_bind("DrawTextW", ctypes.int, win32.HDC, win32.LPCTSTR, ctypes.int, win32.PRECT, win32.UINT); + this.DT_TOP = 0x00000000; + this.DT_LEFT = 0x00000000; + this.DT_CENTER = 0x00000001; + this.DT_RIGHT = 0x00000002; + this.DT_VCENTER = 0x00000004; + this.DT_BOTTOM = 0x00000008; + this.DT_WORDBREAK = 0x00000010; + this.DT_SINGLELINE = 0x00000020; + this.DT_EXPANDTABS = 0x00000040; + this.DT_TABSTOP = 0x00000080; + this.DT_NOCLIP = 0x00000100; + this.DT_EXTERNALLEADING = 0x00000200; + this.DT_CALCRECT = 0x00000400; + this.DT_NOPREFIX = 0x00000800; + this.DT_INTERNAL = 0x00001000; } diff --git a/src/modules/ctypes/winnt/win32.jsm b/src/modules/ctypes/winnt/win32.jsm index ecb7266..dc71e30 100644 --- a/src/modules/ctypes/winnt/win32.jsm +++ b/src/modules/ctypes/winnt/win32.jsm @@ -102,6 +102,16 @@ var win32 = new function() { this.WM_MOUSELAST = 0x020D; this.WM_MOUSELAST = 0x020A; + this.BITMAP = ctypes.StructType("BITMAP", [ + { "bmType": this.LONG }, + { "bmWidth": this.LONG }, + { "bmHeight": this.LONG }, + { "bmWidthBytes": this.LONG }, + { "bmPlanes": this.WORD }, + { "bmBitsPixel": this.WORD }, + { "bmBits": this.LPVOID } + ]); + this.ICONINFO = ctypes.StructType("ICONINFO", [ { "fIcon": this.BOOL }, { "xHotspot": this.DWORD }, @@ -110,6 +120,15 @@ var win32 = new function() { { "hbmColor": this.HBITMAP } ]); this.PICONINFO = this.ICONINFO.ptr; + + this.RECT = ctypes.StructType("RECT", [ + { "left": this.LONG }, + { "top": this.LONG }, + { "right": this.LONG }, + { "bottom": this.LONG } + ]); + this.PRECT = this.RECT.ptr; + }; // ShellAPI.h diff --git a/src/modules/winnt/FiretrayStatusIcon.jsm b/src/modules/winnt/FiretrayStatusIcon.jsm index 8ae1d6a..fb13abd 100644 --- a/src/modules/winnt/FiretrayStatusIcon.jsm +++ b/src/modules/winnt/FiretrayStatusIcon.jsm @@ -20,7 +20,7 @@ Cu.import("resource://firetray/ctypes/winnt/shell32.jsm"); Cu.import("resource://firetray/ctypes/winnt/user32.jsm"); Cu.import("resource://firetray/winnt/FiretrayWin32.jsm"); Cu.import("resource://firetray/commons.js"); -firetray.Handler.subscribeLibsForClosing([kernel32, shell32, user32]); +firetray.Handler.subscribeLibsForClosing([gdi32, kernel32, shell32, user32]); let log = firetray.Logging.getLogger("firetray.StatusIcon"); @@ -194,9 +194,6 @@ firetray.StatusIcon = { break; case win32.WM_RBUTTONUP: log.debug("WM_RBUTTONUP"); - let hicon = firetray.StatusIcon.createSmallIcon(hWnd, "100", "#990000"); - log.debug("%%%%%%%%%% ICON="+hicon); - firetray.StatusIcon.setImageFromIcon(hicon); break; case win32.WM_CONTEXTMENU: log.debug("WM_CONTEXTMENU"); @@ -269,29 +266,50 @@ firetray.StatusIcon = { }, // http://stackoverflow.com/questions/457050/how-to-display-text-in-system-tray-icon-with-win32-api - createSmallIcon: function(hWnd, text, color) { + createTextIcon: function(hWnd, text, color) { + log.debug("createTextIcon hWnd="+hWnd+" text="+text+" color="+color); + + let blank = this.bitmaps.get('blank-icon'); + let bitmap = new win32.BITMAP(); + let err = gdi32.GetObjectW(blank, win32.BITMAP.size, bitmap.address()); // get bitmap info + let width = bitmap.bmWidth, height = bitmap.bmHeight; + let hdc = user32.GetDC(hWnd); // get device context (DC) for hWnd let hdcMem = gdi32.CreateCompatibleDC(hdc); // creates a memory device context (DC) compatible with hdc (need a bitmap) - let hBitmap = this.bitmaps.get('blank-icon'); - let hBitmapMask = gdi32.CreateCompatibleBitmap(hdc, 32, 32); + let hBitmap = user32.CopyImage(blank, user32.IMAGE_BITMAP, width, height, 0); + let hBitmapMask = gdi32.CreateCompatibleBitmap(hdc, width, height); user32.ReleaseDC(hWnd, hdc); - let hOldBitmap = ctypes.cast(gdi32.SelectObject(hdcMem, hBitmap), // replace bitmap in hdcMem by hBitmap - win32.HBITMAP); + let hBitmapOrig = gdi32.SelectObject(hdcMem, hBitmap); // gdi32.PatBlt(hdcMem, 0, 0, 16, 16, gdi32.BLACKNESS); // paint black rectangle +// http://forums.codeguru.com/showthread.php?379565-Windows-SDK-GDI-How-do-I-choose-a-font-size-to-exactly-fit-a-string-in-a + let nHeight = 32, fnWeight = gdi32.FW_BOLD; let hFont = gdi32.CreateFontW(nHeight, 0, 0, 0, fnWeight, 0, 0, 0, gdi32.ANSI_CHARSET, 0, 0, 0, gdi32.FF_SWISS, "Sans"); // get font hFont = ctypes.cast(gdi32.SelectObject(hdcMem, hFont), win32.HFONT); // replace font in bitmap by hFont gdi32.SetTextColor(hdcMem, win32.COLORREF(this.cssColorToCOLORREF(color))); - // gdi32.SetBkMode(hdcMem, gdi32.TRANSPARENT); // VERY IMPORTANT + gdi32.SetBkMode(hdcMem, gdi32.TRANSPARENT); // VERY IMPORTANT // gdi32.SetTextAlign(hdcMem, gdi32.GetTextAlign(hdcMem) & (~gdi32.TA_CENTER)); // gdi32.SetTextAlign(hdcMem, gdi32.TA_CENTER); log.debug(" ___ALIGN=(winLastError="+ctypes.winLastError+") "+gdi32.GetTextAlign(hdcMem)); - gdi32.TextOutW(hdcMem, 0, 0, text, text.length); - gdi32.SelectObject(hdcMem, hOldBitmap); // always replace new hBitmap with original one + let size = new gdi32.SIZE(); + // GetTextExtentPoint32 is known as more reliable than DrawText(DT_CALCRECT) + gdi32.GetTextExtentPoint32W(hdcMem, text, text.length, size.address()); + let nWidth = size.cx; + log.debug(" WIDTH="+nWidth); + + // let rect = new win32.RECT(); + // let height = user32.DrawTextW(hdcMem, text, text.length, rect.address(), user32.DT_SINGLELINE | user32.DT_CENTER | user32.DT_VCENTER | user32.DT_CALCRECT); + // log.debug(" HEIGHT="+height+", rect="+rect); + + let nXStart = firetray.js.floatToInt((width - nWidth)/2), + nYStart = firetray.js.floatToInt((height - nHeight)/2); + gdi32.TextOutW(hdcMem, nXStart, nYStart, text, text.length); // ref point for alignment + + gdi32.SelectObject(hdcMem, hBitmapOrig); let iconInfo = win32.ICONINFO(); iconInfo.fIcon = true; @@ -301,6 +319,7 @@ firetray.StatusIcon = { iconInfo.hbmColor = hBitmap; let hIcon = user32.CreateIconIndirect(iconInfo.address()); + log.debug(" CreateIconIndirect hIcon="+hIcon+" lastError="+ctypes.winLastError); gdi32.DeleteObject(gdi32.SelectObject(hdcMem, hFont)); gdi32.DeleteDC(hdcMem); @@ -332,6 +351,12 @@ firetray.Handler.setIconTooltipDefault = function() { }; firetray.Handler.setIconText = function(text, color) { + let hicon = firetray.StatusIcon.createTextIcon( + firetray.StatusIcon.hwndProxy, text, color); + log.debug("setIconText icon="+hicon); + if (hicon.isNull()) + log.error("Could not create hicon"); + firetray.StatusIcon.setImageFromIcon(hicon); }; firetray.Handler.setIconVisibility = function(visible) {