/* 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_BYCOMMAND = 0x0; var MF_BYPOSITION = 0x400; var MF_BITMAP = 0x4; var MF_DEFAULT = 0x1000; var MNS_CHECKORBMP = 0x4000000; var MIM_APPLYTOSUBMENUS = 0x80000000; var MIM_BACKGROUND = 0x2; var MIM_STYLE = 0x10; var MIIM_ID = 0x2; var MIIM_STATE = 0x1; var MIIM_STRING = 0x40; var MIIM_SUBMENU = 0x4; var MIIM_BITMAP = 0x80; var TPM_LEFTALIGN = 0x0; var TPM_RETURNCMD = 0x0100; var TPM_VERNEGANIMATION = 0x2000; //Other menu globals var MENU_ID_BASE = 0x1337; var subclass = MsgPlus.CreateWnd('Interfaces\\WndSubclass.xml', 'WndSubclass', 2); /** * 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. * 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 : new Function(); this.Index = (typeof this.Owner === "object") ? 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); //Separators are handled by AddSeparator() if(nState & MF_SEPARATOR) return this.AddSeparator(); var nId = MENU_ID_BASE + this.IndexInc(sId); var fMask = MIIM_STRING | MIIM_ID | MIIM_STATE; var fState = (nState & (MF_CHECKED | MF_DISABLED | MF_GRAYED)) | (bIsDefault ? MF_DEFAULT : 0); var hBitmap = 0; //Set the bitmap if(sBitmapPath.length > 0 && FileExists(sBitmapPath)) { var hBitmap = this.CreateBitmap(sBitmapPath); fMask |= MIIM_BITMAP; } //dwTypeData is a string pointer var dwTypeData = Interop.Allocate(2*sLabel.length+2); dwTypeData.WriteString(0, sLabel); //Fill a MENUITEMINFO structure var MENUITEMINFO = Interop.Allocate(48); with(MENUITEMINFO) { WriteDWORD(0, Size); WriteDWORD(4, fMask); WriteDWORD(12, fState); WriteDWORD(16, nId); WriteDWORD(36, dwTypeData.DataPtr); WriteDWORD(40, sLabel.length); WriteDWORD(44, hBitmap); } //Add the item var bResult = Interop.Call("user32", "InsertMenuItemW", this.Handle, -1, false, MENUITEMINFO); MENUITEMINFO.Size = 0; dwTypeData.Size = 0; //Return the result return bResult; }, /** * Adds a separator to the menu. * Return value: True indicates success, otherwise false. */ "AddSeparator" : function() { return Interop.Call("user32", "AppendMenuW", this.Handle, MF_SEPARATOR, 0, 0); }, /** * 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 : ""; var fMask = MIIM_STRING | MIIM_STATE | MIIM_SUBMENU; var fState = nState & (MF_CHECKED | MF_DISABLED | MF_GRAYED); var hBitmap = 0; //Set the bitmap if(sBitmapPath.length > 0 && FileExists(sBitmapPath)) { var hBitmap = this.CreateBitmap(sBitmapPath); fMask |= MIIM_BITMAP; } //dwTypeData is a string pointer var dwTypeData = Interop.Allocate(2*sLabel.length+2); dwTypeData.WriteString(0, sLabel); //Fill a MENUITEMINFO structure var MENUITEMINFO = Interop.Allocate(48); with(MENUITEMINFO) { WriteDWORD(0, Size); WriteDWORD(4, fMask); WriteDWORD(12, fState); WriteDWORD(20, SubMenu.Handle); WriteDWORD(36, dwTypeData.DataPtr); WriteDWORD(40, sLabel.length); WriteDWORD(44, hBitmap); } //Add the item var bResult = Interop.Call("user32", "InsertMenuItemW", this.Handle, -1, false, MENUITEMINFO); MENUITEMINFO.Size = 0; dwTypeData.Size = 0; //Return the result return bResult; }, /** * 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 == true); var nId = 0; if(typeof Id === "string") { //Look up Id in ItemIDs var nId = this.InArray(this.ItemIDs, Id); if(nId === -1) nIndex = 0; } else nId = 1*Id; Interop.Call("user32", "CheckMenuItem", this.Handle, (MENU_ID_BASE + nId), (bCheck ? MF_CHECKED : 0)); }, /** * Checks and makes a menu item a radio-item and clears all other checkmarks in the group. * Id Menu item position (number) or menu item ID (string) to change. * bCheck Boolean defining the new check state. * Return value: None (void) */ "CheckMenuRadioItem" : function(CheckId, FirstId, LastId) { //idFirst var nIdCheck = 0; if(typeof CheckId === "string") { nIdCheck = this.InArray(this.ItemIDs, CheckId); if(nIdCheck === -1) nIdCheck = 0; } else nIdCheck = 1*CheckId; //idFirst var nFirstId = 0; if(typeof FirstId === "number") { nFirstId = FirstId; } else if(typeof FirstId === "string") { nFirstId = this.InArray(this.ItemIDs, FirstId); if(nFirstId === -1) nFirstId = 0; } //idLast var nLastId = -1; if(typeof LastId === "number") { nLastId = LastId; } else if(typeof LastId === "string") { nLastId = this.InArray(this.ItemIDs, LastId); } if(nLastId === -1) nLastId = this.ItemIDs.length-1; Interop.Call("user32", "CheckMenuRadioItem", this.Handle, (MENU_ID_BASE + nFirstId), (MENU_ID_BASE + nLastId), (MENU_ID_BASE + nIdCheck), MF_BYCOMMAND); }, /** * 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. * nColor COLORREF (0x00bbggrr) defining the background color of the menu. Optional. * Return value: True indicates success, otherwise false. */ "Open" : function(oPt, oParam, 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 with(POINTAPI) { WriteDWORD(0, oPt[0]); WriteDWORD(4, oPt[1]); } } else { //Set current cursor position Interop.Call("user32", "GetCursorPos", POINTAPI); } //Set the background color of the menu if(typeof nColor === "number") { var hBrush = Interop.Call("gdi32", "CreateSolidBrush", nColor); var MENUINFO = Interop.Allocate(28); with(MENUINFO) { WriteDWORD(0, Size); //cbSize WriteDWORD(4, MIM_APPLYTOSUBMENUS | MIM_BACKGROUND); //fMask 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 Result = Interop.Call("user32", "TrackPopupMenu", this.Handle, TPM_LEFTALIGN | TPM_RETURNCMD | TPM_VERNEGANIMATION, POINTAPI.ReadDWORD(0), POINTAPI.ReadDWORD(4), 0, subclass.Handle, 0); POINTAPI.Size = 0; this.Callback((Result ? this.ItemIDs[(Result - MENU_ID_BASE)] : -1), oParam); return true; }, /** * Destroys the menu. * Return value: True indicates success, otherwise false. */ "Destroy" : function() { //Destroy submenus first. if(this.SubMenus.length > 0) { for(var i=0; i