/* * ----- * Screenshot Sender - __menu.js * ----- * Menu class to create Windows menus. * ----- */ //Message constants for menu var MF_CHECKED = 0x8; var MF_UNCHECKED = 0x0; var MF_ENABLED = 0x0; var MF_DISABLED = 0x2; var MF_GRAYED = 0x1; var MF_APPEND = 0x100; var MF_SEPARATOR = 0x800; var MF_STRING = 0x0; var MF_POPUP = 0x10; var MF_BYPOSITION = 0x400; var MF_BITMAP = 0x4; var TPM_LEFTALIGN = 0x0; var TPM_RIGHTALIGN = 0x8; var TPM_RETURNCMD = 0x0100; var TPM_VERNEGANIMATION = 0x2000; var TPM_LAYOUTRTL = 0x8000; var MIIM_STRING = 0x40; var MIIM_SUBMENU = 0x4; var MIM_APPLYTOSUBMENUS = 0x80000000; var MIM_BACKGROUND = 0x2; var MIM_STYLE = 0x10; var MNS_CHECKORBMP = 0x4000000; //Other menu globals var MENU_ID_BASE = 0x1337; var subclass; /** * MENU CLASS - by Mattias Buelens * This class lets you create and open Windows menus. You can even add submenus and images to the menu. * Requirements: * The Gdi.js file of Countdown Live 2 for the bitmap functions. Note that this file contains Trace() function calls. You can * either replace these calls by Debug.Trace() calls and remove the last parameter, or you can simply remove these lines or copy * the Trace() function into your script. I really don't care. :-P * Usage: * 1. Create a new instance with "new Menu() " with the fCallback parameter, which should be a function to receive the menu clicks. * 2. Now, you can: * - Add menu items with "Menu.AddMenuItem() ". * - Add submenus: * 1. Create a new submenu with "Menu.CreateSubMenu() ". * 2. Add stuff in this submenu, for example by using "SubMenu.AddMenuItem() ". You can even add sub-submenus! * 3. Add the submenu to its parent with "Menu.AddSubMenu() ". * - Add menu separators with "Menu.AddSeparator() ". * 3. You can make the menu pop up anywhere you want by calling "Menu.Open() ". * 4. The callback function will receive up to 2 parameters: * - A string specifying the ID of the menu item clicked. This is the ID given to an item in "Menu.AddMenuItem() ". * - An extra parameter passed through "Menu.Open() ". * It can be anything you want and may be useful if you use the same menu on various places. * 5. When the menu isn't needed anymore, call "Menu.Destroy() " to destroy it along with any bitmaps or submenus that were created. * You can use and modify this class in any way you like, as long as you keep the appropriate credits in the script. * - The GDI and GDI+ functions for the bitmaps were originally written by Matty(Screenshot Sender developer) and further * modified by myself. * - The class was written by me. You don't need to place this whole header, just a small note will do just fine. :-) * Other than that, feel free to do whatever you want with this. Hopefully this class can be useful to you one day and will let you * create beautiful menus for your scripts. :-) * * - Mattias Buelens. */ var Menu = function (fCallback, oMenu) { this.Owner = (typeof oMenu == "object") ? oMenu : false; this.Callback = (typeof fCallback == "function") ? fCallback : function () { }; this.Index = (this.Owner != false) ? this.Owner.Index : 0; this.ItemIDs = [], this.Bitmaps = {}, this.SubMenus = []; this.Handle = Interop.Call("user32", "CreatePopupMenu"); this.SetMenuInfo(); } Menu.prototype = { /** * Adds a menu item to the menu. * sId Unique string identifier for this menu item, used to identify it when clicked. * sLabel The label for the new menu item. Ignored when nState is MF_SEPARATOR. Optional. * nState The state of the menu item. Optional. * Possible values: MF_CHECKED, MF_UNCHECKED, MF_ENABLED, MF_DISABLED, MF_GRAYED, MF_SEPARATOR * sBitmapPath The path to an image file to add to the menu item. Optional. * bIsDefault Boolean defining whether this is the default menu item. Optional. * Return value: True indicates success, otherwise false. */ "AddMenuItem" : function (sId, sLabel, nState, sBitmapPath, bIsDefault) { sId = (typeof sId != "undefined") ? sId : 0 sLabel = (typeof sLabel == "string") ? sLabel : ""; nState = (typeof nState == "number") ? nState : 0; sBitmapPath = (typeof sBitmapPath == "string") ? sBitmapPath : ""; bIsDefault = (bIsDefault == true); var nIndex = (this.Owner == false) ? this.Index : this.Owner.Index - this.Index; //When this is a separator, add it and stop. if (nState & MF_SEPARATOR) { var bResult = Interop.Call("user32", "AppendMenuW", this.Handle, MF_SEPARATOR, MENU_ID_BASE + this.Index, 0); if (bResult == 0) return false; this.IndexInc(sId); return true; } //Add the menu item as a string. Interop.Call("user32", "AppendMenuW", this.Handle, MF_STRING, MENU_ID_BASE + this.Index, sLabel); if (bResult == 0) return false; //Sets the menu item to default when asked. if (bIsDefault) Interop.Call("user32", "SetMenuDefaultItem", this.Handle, nIndex, true); //Add a bitmap if one is specified. if (sBitmapPath.length > 0 && FileExists(sBitmapPath) ) { var bResult = this.SetBitmap(sBitmapPath, nIndex); } //Sets the item states if (typeof nState == "number" && nState > 0) { if (nState & MF_CHECKED) Interop.Call("user32", "CheckMenuItem", this.Handle, nIndex, MF_BYPOSITION | MF_CHECKED); if (nState & MF_DISABLED) Interop.Call("user32", "EnableMenuItem", this.Handle, nIndex, MF_BYPOSITION | MF_DISABLED); if (nState & MF_GRAYED) Interop.Call("user32", "EnableMenuItem", this.Handle, nIndex, MF_BYPOSITION | MF_GRAYED); } this.IndexInc(sId); return true; }, /** * Adds a separator to the menu. * Return value: True indicates success, otherwise false. */ "AddSeparator" : function () { return this.AddMenuItem("", "", MF_SEPARATOR, "", false); }, /** * Creates a new submenu. * Return value: The new submenu. */ "CreateSubMenu" : function () { return new Menu(0, this); }, /** * Adds a submenu to the menu. * SubMenu The menu object to add as submenu. * sLabel The label for the submenu. Optional. * nState The state of the submenu. See AddMenuItem. Optional. * sBitmapPath The path to an image file to add to the submenu. Optional. * Return value: True indicates success, otherwise false. */ "AddSubMenu" : function (SubMenu, sLabel, nState, sBitmapPath) { sLabel = (typeof sLabel == "string") ? sLabel : ""; nState = (typeof nState == "number") ? nState : 0; sBitmapPath = (typeof sBitmapPath == "string") ? sBitmapPath : ""; //Add the submenu var bResult = Interop.Call("user32", "AppendMenuW", this.Handle, MF_POPUP | MF_STRING, SubMenu.Handle, sLabel); if (bResult == 0) return false; //Set the owner of the submenu var _this = this; SubMenu.Owner = _this; //Add the submenu to the list of submenus this.SubMenus.push(SubMenu); //Add a bitmap if one is specified. if (sBitmapPath.length > 0 && FileExists(sBitmapPath) ) { var bResult = this.SetBitmap(sBitmapPath, this.Index); } //Sets the item states if (typeof nState == "number" && nState > 0) { if (nState & MF_CHECKED) Interop.Call("user32", "CheckMenuItem", this.Handle, this.Index, MF_BYPOSITION | MF_CHECKED); if (nState & MF_DISABLED) Interop.Call("user32", "EnableMenuItem", this.Handle, this.Index, MF_BYPOSITION | MF_DISABLED); if (nState & MF_GRAYED) Interop.Call("user32", "EnableMenuItem", this.Handle, this.Index, MF_BYPOSITION | MF_GRAYED); } this.IndexInc(""); return true; }, /** * Sets the state of the specified menu item's check-mark attribute. * Id Menu item position(number) or menu item ID(string) to change. * bCheck Boolean defining the new check state. * Return value: None(void) */ "CheckMenuItem" : function (Id, bCheck) { bCheck = (typeof bCheck == "undefined") ? true : (bCheck != false); var nId = 0; if (typeof Id == "string") { //Look up Id in ItemIDs var i = this.ItemIDs.length; while (i--) { if (Id == this.ItemIDs[i]) { nId = i; break; } } } else nId = 1*Id; Interop.Call("user32", "CheckMenuItem", this.Handle, nId, MF_BYPOSITION | (bCheck ? MF_CHECKED : 0) ); }, /** * Opens the menu. * oPt Array of the form[x,y]where x and y are the coordinates to open the menu. Optional. * oParam Parameter to pass to the callback function with the menu item ID. Optional. * bRTL Boolean specifying if the menu should be positioned for an RTL window. Optional. * nColor COLORREF(0x00bbggrr) defining the background color of the menu. Optional. * Return value: True indicates success, otherwise false. */ "Open" : function (oPt, oParam, bRTL, nColor) { oParam = (typeof oParam != "undefined") ? oParam : undefined; if (!this.IsMenuValid() ) return false; //Set the position var POINTAPI = Interop.Allocate(8); if (typeof oPt == "object") { //Set given position POINTAPI.WriteDWORD(0, oPt[0]); POINTAPI.WriteDWORD(4, oPt[1]); } else { //Set current cursor position Interop.Call("user32", "GetCursorPos", POINTAPI); } //Get the subclass window if (!subclass) subclass = MsgPlus.CreateWnd('Languages\\en\\Subclass.xml', 'Subclass', /*WNDOPT_INVISIBLE*/ 2); //Set the background color of the menu if (typeof nColor == "number") { var hBrush = Interop.Call("gdi32", "CreateSolidBrush", nColor); var MENUINFO = Interop.Allocate(28); MENUINFO.WriteDWORD(0, Size); //cbSize MENUINFO.WriteDWORD(4, MIM_APPLYTOSUBMENUS | MIM_BACKGROUND); //fMask MENUINFO.WriteDWORD(16, hBrush); //hBrush var Result = Interop.Call("user32", "SetMenuInfo", this.Handle, MENUINFO); MENUINFO.Size = 0; Interop.Call("gdi32", "DeleteObject", hBrush); } //Open and retrieve clicked item var Flags = TPM_RETURNCMD | TPM_VERNEGANIMATION; if (typeof bRTL != "undefined") Flags |= bRTL ? TPM_RIGHTALIGN | TPM_LAYOUTRTL : TPM_LEFTALIGN; var Result = Interop.Call("user32", "TrackPopupMenu", this.Handle, Flags, POINTAPI.ReadDWORD(0), POINTAPI.ReadDWORD(4), 0, subclass.Handle, 0); POINTAPI.Size = 0; this.Callback( (Result == 0) ? -1 : this.ItemIDs[(Result - MENU_ID_BASE) ], oParam); return true; }, /** * Destroys the menu. * Return value: True indicates success, otherwise false. */ "Destroy" : function () { //Destroy submenus first. if (this.SubMenus.length > 0) { var i = this.SubMenus.length; while (i--) this.SubMenus[i].Destroy(); } //Check if this menu can be destroyed. if (!this.IsMenuValid()) return false; //Destroy the menu. Interop.Call("user32", "DestroyMenu", this.Handle); this.Handle = 0; //Destroys all bitmaps associated with menu items. if (typeof this.Bitmaps === "object") { var i = this.Bitmaps.length; while (i--) DestroyBitmap(this.Bitmaps[i]); } return true; }, /** * Increases the menu's index and the index of its owner. */ "IndexInc" : function (sItemID) { var nIndex = (this.Owner == false) ? this.Index : this.Owner.Index - this.Index; this.ItemIDs[nIndex] = sItemID; this.Index++; if (this.Owner != false) this.Owner.IndexInc(sItemID); }, /** * Creates and sets the bitmap to a menu item. Used internally. * sBitmapPath The path to an image file to add to the item. * uItem The numeric identifier of the item. * Return value: True indicates success, otherwise false. */ "SetBitmap" : function (sBitmapPath, uItem) { //Create the bitmap if it doesn't exist yet var hBitmap = 0; if (typeof this.Bitmaps[sBitmapPath] === "number") { hBitmap = this.Bitmaps[sBitmapPath]; } else { hBitmap = GdiCreateHBITMAPFromFile(sBitmapPath); if (hBitmap == false) { DestroyBitmap(hBitmap); this.Bitmaps[sBitmapPath] = false; return false; } else { this.Bitmaps[sBitmapPath] = hBitmap; } } //Set the bitmap to the menu item var MIIM_BITMAP = 0x80; var MENUITEMINFO = Interop.Allocate(48); MENUITEMINFO.WriteDWORD(0, Size); MENUITEMINFO.WriteDWORD(4, MIIM_BITMAP); MENUITEMINFO.WriteDWORD(44, hBitmap); var Result = Interop.Call("user32", "SetMenuItemInfoW", this.Handle, uItem, true, MENUITEMINFO); if (Result == 0) Debug.Trace("!!! Error occured while setting bitmap for item "+uItem+"."); MENUITEMINFO.Size = 0; return(Result != 0); }, /** * Sets the necessary properties of the menu. Used internally. * Return value: True indicates success, otherwise false. */ "SetMenuInfo" : function () { var MENUINFO = Interop.Allocate(28); MENUINFO.WriteDWORD(0, MENUINFO.Size); var Result = Interop.Call("user32", "GetMenuInfo", this.Handle, MENUINFO); if (Result == 0) { MENUINFO.Size = 0; return false; } with (MENUINFO) { WriteDWORD(4, MIM_STYLE); WriteDWORD(8, ReadDWORD(8) | MNS_CHECKORBMP); } var Result = Interop.Call("user32", "SetMenuInfo", this.Handle, MENUINFO); MENUINFO.Size = 0; return(Result == 0); }, /** * Checks if the current menu handle is valid. Used internally. */ "IsMenuValid" : function () { return(typeof this.Handle == "number" && Interop.Call("user32", "IsMenu", this.Handle) != 0); } }