private void AddSubMenu( MenuCommand parentMenuCommand, Menu.MenuItemCollection items ) { for ( int i = 0; i < items.Count; i++ ) { // I know these menu items are actually MenuItemExs MenuItemEx item = (MenuItemEx)items[i]; Bitmap bmp = ( item.Icon != null ) ? (Bitmap)item.Icon : ( ( item.ImageList != null ) ? (Bitmap)item.ImageList.Images[ item.ImageIndex ] : null ); EventHandler hndl = item.ClickHandler; // if menu item does not have any ClickHandler then attach own if( hndl == null ) { hndl = new EventHandler( RaiseMenuItemClick ); } MenuCommand currentMenuCommand = new MenuCommand(item.Text, bmp, (Shortcut)item.Shortcut, hndl, item); currentMenuCommand.Checked = item.Checked; currentMenuCommand.Enabled = item.Enabled; parentMenuCommand.MenuCommands.Add(currentMenuCommand); if ( item.MenuItems.Count > 0 ) AddSubMenu(currentMenuCommand, item.MenuItems); } }
public MenuCommand Add(MenuCommand value) { // Use base class to process actual collection operation base.List.Add(value as object); return value; }
public void InternalConstruct(MenuCommand command, Rectangle drawRect, int row, int col) { _row = row; _col = col; _enabled = command.Enabled; _expansion = false; _vertSeparator = false; _drawRect = drawRect; _command = command; _chevron = false; // Is this MenuCommand a separator? _separator = (_command.Text == "-"); // Does this MenuCommand contain a submenu? _subMenu = (_command.MenuCommands.Count > 0); // Find position of first mnemonic character int position = command.Text.IndexOf('&'); // Did we find a mnemonic indicator? if (position != -1) { // Must be a character after the indicator if (position < (command.Text.Length - 1)) { // Remember the character _mnemonic = char.ToUpper(command.Text[position + 1]); } } }
public DrawCommand(Rectangle drawRect, bool expansion) { _row = -1; _col = -1; _mnemonic = '0'; _enabled = true; _subMenu = false; _expansion = expansion; _separator = !expansion; _vertSeparator = !expansion; _chevron = false; _drawRect = drawRect; _command = null; }
protected void OnWM_DISMISS() { // Pass on to any child menu of ours if ( childMenu != null) { // Inform the child menu it is no longer needed WindowsAPI.PostMessage( childMenu.Handle, WM_DISMISS, 0, 0); } // Define the selection to return to caller returnCommand = null; // Finish processing messages timer.Stop(); exitLoop = true; // Hide ourself WindowsAPI.ShowWindow(this.Handle, (short)Win32.ShowWindowStyles.SW_HIDE); // Kill ourself DestroyHandle(); }
protected void OnWM_XBUTTONUP(ref Message m) { // Extract the mouse position int xPos = (int)((uint)m.LParam & 0x0000FFFFU); int yPos = (int)(((uint)m.LParam & 0xFFFF0000U) >> 16); Point pos = new Point(xPos, yPos); for(int i=0; i< drawCommands.Count; i++) { DrawCommand dc = drawCommands[i] as DrawCommand; if (dc.DrawRect.Contains(pos)) { // Is there a change in selected item? if ( trackItem != i) { // Modify the display of the two items SwitchSelection( trackItem, i, false, false); } } } // Is an item selected? if ( trackItem != -1) { DrawCommand dc = drawCommands[ trackItem] as DrawCommand; // Does this item have a submenu? if (dc.SubMenu) { // If we are not already showing this submenu... if ( popupItem != trackItem) { // Is a submenu for a different item showing? if ( childMenu != null) { // Inform the child menu it is no longer needed WindowsAPI.PostMessage( childMenu.Handle, WM_DISMISS, 0, 0); } // Handle the submenu OperateSubMenu( trackItem, false); } } else { if (dc.Expansion) RegenerateExpansion(); else { // Kill any child menus open if ( childMenu != null) { // Inform the child menu it is no longer needed WindowsAPI.PostMessage( childMenu.Handle, WM_DISMISS, 0, 0); } // Define the selection to return to caller returnCommand = dc.MenuCommand; // Finish processing messages timer.Stop(); exitLoop = true; } } } }
protected void OperateSubMenu(int popupItem, bool selectFirst) { popupItem = popupItem; childMenu = new PopupMenu(); DrawCommand dc = drawCommands[popupItem] as DrawCommand; // Find screen coordinate of Top right of item cell Win32.POINT screenPosTR; screenPosTR.x = dc.DrawRect.Right; screenPosTR.y = dc.DrawRect.Top; WindowsAPI.ClientToScreen(this.Handle, ref screenPosTR); // Find screen coordinate of top left of item cell Win32.POINT screenPosTL; screenPosTL.x = dc.DrawRect.Left; screenPosTL.y = dc.DrawRect.Top; WindowsAPI.ClientToScreen(this.Handle, ref screenPosTL); // Ensure the child has the same properties as ourself childMenu.Style = this.Style; childMenu.Font = this.Font; // Record keyboard direction int returnDir = 0; returnCommand = childMenu.InternalTrackPopup(new Point(screenPosTR.x, screenPosTR.y), new Point(screenPosTL.x, screenPosTL.y), dc.MenuCommand.MenuCommands, this, selectFirst, parentControl, popupRight, popupDown, ref returnDir); popupItem = -1;; childMenu = null; if (( returnCommand != null) || (returnDir != 0)) { // Finish processing messages timer.Stop(); exitLoop = true; returnDir = returnDir; } }
protected void Recalculate() { int length; if (_direction == Direction.Horizontal) length = this.Width; else length = this.Height; // Is there space for any commands? if (length > 0) { // Count the number of rows needed int rows = 0; // Number of items on this row int columns = 0; // Create a collection of drawing objects _drawCommands = new ArrayList(); // Minimum length is a gap on either side of the text int cellMinLength = _lengthGap * 2; // Each cell is as broad as the whole control int cellBreadth = this.Height; // Accumulate starting position of each cell int lengthStart = 0; // If the chevron is already displayed then reduce length by its length if (_chevronStartCommand != null) length -= cellMinLength + _chevronLength; // Assume chevron is not needed by default _chevronStartCommand = null; using(Graphics g = this.CreateGraphics()) { // Count the item we are processing int index = 0; foreach(MenuCommand command in _menuCommands) { // Give the command a chance to update its state command.OnUpdate(EventArgs.Empty); // Ignore items that are marked as hidden if (!command.Visible) continue; int cellLength = 0; // Is this a separator? if (command.Text == "-") cellLength = _separatorWidth; else { // Calculate the text width of the cell SizeF dimension = g.MeasureString(command.Text, this.Font); // Always add 1 to ensure that rounding is up and not down cellLength = cellMinLength + (int)dimension.Width + 1; } Rectangle cellRect; // Create a new position rectangle if (_direction == Direction.Horizontal) cellRect = new Rectangle(lengthStart, _rowHeight * rows, cellLength, _rowHeight); else cellRect = new Rectangle(_rowWidth * rows, lengthStart, _rowWidth, cellLength); lengthStart += cellLength; columns++; // If this item is overlapping the control edge and it is not the first // item on the line then we should wrap around to the next row. if ((lengthStart > length) && (columns > 1)) { if (_multiLine) { // Move to next row rows++; // Reset number of items on this column columns = 1; // Reset starting position of next item lengthStart = cellLength; // Reset position of this item if (_direction == Direction.Horizontal) { cellRect.X = 0; cellRect.Y += _rowHeight; } else { cellRect.X += _rowWidth; cellRect.Y = 0; } } else { // Is a tracked item being make invisible if (index <= _trackItem) { // Need to remove tracking of this item _trackItem = -1; } // Remember which item is first for the chevron submenu _chevronStartCommand = command; if (_direction == Direction.Horizontal) { cellRect.Y = 0; cellRect.Width = cellMinLength + _chevronLength; cellRect.X = this.Width - cellRect.Width; cellRect.Height = _rowHeight; } else { cellRect.X = 0; cellRect.Height = cellMinLength + _chevronLength; cellRect.Y = this.Height - (cellMinLength + _chevronLength); cellRect.Width = _rowWidth; } // Create a draw command for this chevron _drawCommands.Add(new DrawCommand(cellRect)); // Exit, do not add the current item or any afterwards break; } } // Create a drawing object _drawCommands.Add(new DrawCommand(command, cellRect)); index++; } } if (_direction == Direction.Horizontal) { int controlHeight = (rows + 1) * _rowHeight; // Ensure the control is the correct height if (this.Height != controlHeight) this.Height = controlHeight; } else { int controlWidth = (rows + 1) * _rowWidth; // Ensure the control is the correct width if (this.Width != controlWidth) this.Width = controlWidth; } } }
protected void OnPopupEnd(MenuCommand mc) { if (PopupEnd != null) PopupEnd(mc); }
public void Insert(int index, MenuCommand value) { // Use base class to process actual collection operation base.List.Insert(index, value as object); }
public int IndexOf(MenuCommand value) { // Find the 0 based index of the requested entry return base.List.IndexOf(value); }
public bool Contains(MenuCommand value) { // Use base class to process actual collection operation return base.List.Contains(value as object); }
public void AddRange(MenuCommand[] values) { // Use existing method to add each array entry foreach(MenuCommand page in values) Add(page); }
public DrawCommand(MenuCommand command, Rectangle drawRect, int row, int col) { InternalConstruct(command, drawRect, row, col); }
public DrawCommand(MenuCommand command, Rectangle drawRect) { InternalConstruct(command, drawRect, -1, -1); }
protected MenuCommand InternalTrackPopup(bool selectFirst) { // MenuCommand to return as method result returnCommand = null; // No item is being tracked trackItem = -1; // Flag to indicate when to exit the message loop exitLoop = false; // Assume the mouse does not start over our window mouseOver = false; // Direction of key press if this caused dismissal returnDir = 0; // Flag to indicate if the message should be dispatched bool leaveMsg = false; // Create and show the popup window (without taking the focus) CreateAndShowWindow(); // Create an object for storing windows message information Win32.MSG msg = new Win32.MSG(); // Draw everything now... //RefreshAllCommands(); // Pretend user pressed key down to get the first valid item selected if (selectFirst) ProcessKeyDown(); // Process messages until exit condition recognised while(! exitLoop) { // Suspend thread until a windows message has arrived if (WindowsAPI.WaitMessage()) { // Take a peek at the message details without removing from queue while(! exitLoop && WindowsAPI.PeekMessage(ref msg, 0, 0, 0, (int)Win32.PeekMessageFlags.PM_NOREMOVE)) { // Leave messages for children IntPtr hParent = WindowsAPI.GetParent(msg.hwnd); bool child = hParent == Handle; bool combolist = IsComboBoxList(msg.hwnd); // Mouse was pressed in a window of this application if ((msg.message == (int)Msg.WM_LBUTTONDOWN) || (msg.message == (int)Msg.WM_MBUTTONDOWN) || (msg.message == (int)Msg.WM_RBUTTONDOWN) || (msg.message == (int)Msg.WM_NCLBUTTONDOWN) || (msg.message == (int)Msg.WM_NCMBUTTONDOWN) || (msg.message == (int)Msg.WM_NCRBUTTONDOWN)) { // Is the mouse event for this popup window? if (msg.hwnd != this.Handle) { // Let the parent chain of PopupMenu's decide if they want it if (!ParentWantsMouseMessage(ref msg)&& !child && !combolist) { // No, then we need to exit the popup menu tracking exitLoop = true; // DO NOT process the message, leave it on the queue // and let the real destination window handle it. leaveMsg = true; // Is a parent control specified? if ( parentControl != null) { // Is the mouse event destination the parent control? if (msg.hwnd == parentControl.Handle) { // Then we want to consume the message so it does not get processed // by the parent control. Otherwise, pressing down will cause this // popup to disappear but the message will then get processed by // the parent and cause a popup to reappear again. When we actually // want the popup to disappear and nothing more. leaveMsg = false; } } } } } else { // Mouse move occured if (msg.message == (int)Msg.WM_MOUSEMOVE) { // Is the mouse event for this popup window? if (msg.hwnd != this.Handle) { // Do we still think the mouse is over our window? if ( mouseOver) { // Process mouse leaving situation OnWM_MOUSELEAVE(); } // Let the parent chain of PopupMenu's decide if they want it if (!ParentWantsMouseMessage(ref msg) && !child && !combolist) { // Eat the message to prevent the destination getting it Win32.MSG eat = new Win32.MSG(); WindowsAPI.GetMessage(ref eat, 0, 0, 0); // Do not attempt to pull a message off the queue as it has already // been eaten by us in the above code leaveMsg = true; } } } else { // Was the alt key pressed? if (msg.message == (int)Msg.WM_SYSKEYDOWN) { // Alt key pressed on its own if((int)msg.wParam == (int)Win32.VirtualKeys.VK_MENU) // ALT key { // Then we should dimiss ourself exitLoop = true; } } // Was a key pressed? if (msg.message == (int)Msg.WM_KEYDOWN) { switch((int)msg.wParam) { case (int)Win32.VirtualKeys.VK_UP: ProcessKeyUp(); break; case (int)Win32.VirtualKeys.VK_DOWN: ProcessKeyDown(); break; case (int)Win32.VirtualKeys.VK_LEFT: ProcessKeyLeft(); break; case (int)Win32.VirtualKeys.VK_RIGHT: if(ProcessKeyRight()) { // Do not attempt to pull a message off the queue as the // ProcessKeyRight has eaten the message for us leaveMsg = true; } break; case (int)Win32.VirtualKeys.VK_RETURN: // Is an item currently selected if ( trackItem != -1) { DrawCommand dc = drawCommands[ trackItem] as DrawCommand; // Does this item have a submenu? if (dc.SubMenu) { // Consume the keyboard message to prevent the submenu immediately // processing the same message again. Remember this routine is called // after PeekMessage but the message is still on the queue at this point Win32.MSG eat = new Win32.MSG(); WindowsAPI.GetMessage(ref eat, 0, 0, 0); // Handle the submenu OperateSubMenu( trackItem, false); // Do not attempt to pull a message off the queue as it has already // been eaten by us in the above code leaveMsg = true; } else { // Is this item the expansion command? if (dc.Expansion) { RegenerateExpansion(); } else { // Define the selection to return to caller returnCommand = dc.MenuCommand; // Finish processing messages exitLoop = true; } } } break; case (int)Win32.VirtualKeys.VK_ESCAPE: // User wants to exit the menu, so set the flag to exit the message loop but // let the message get processed. This way the key press is thrown away. exitLoop = true; break; default: // Any other key is treated as a possible mnemonic int selectItem = ProcessMnemonicKey((char)msg.wParam); if (selectItem != -1) { DrawCommand dc = drawCommands[selectItem] as DrawCommand; // Define the selection to return to caller returnCommand = dc.MenuCommand; // Finish processing messages exitLoop = true; } break; } } } } // Should the message we pulled from the queue? if (!leaveMsg) { if (WindowsAPI.GetMessage(ref msg, 0, 0, 0)) { WindowsAPI.TranslateMessage(ref msg); WindowsAPI.DispatchMessage(ref msg); } } else leaveMsg = false; } } } // Do we have a focus we need to restore? if ( oldFocus != IntPtr.Zero) ReturnTheFocus(); // Need to unset this window as the parent of the comboboxes // -- if any -- otherwise the combobox use in an toolbar would get "sick" UnsetComboBoxesParent(); // Hide the window from view before killing it, as sometimes there is a // short delay between killing it and it disappearing because of the time // it takes for the destroy messages to get processed WindowsAPI.ShowWindow(this.Handle, (short)Win32.ShowWindowStyles.SW_HIDE); // Commit suicide DestroyHandle(); // Was a command actually selected? if (( parentMenu == null) && ( returnCommand != null)) { // Pulse the selected event for the command returnCommand.OnClick(EventArgs.Empty); } return returnCommand; }
private void Attach() { // Cleanup previous menus MenuCommands.Clear(); foreach( ToolBarItem item in m_items ) { string text = item.Text; if ( text == string.Empty || text == null ) text = item.ToolTip; if ( item.Style == ToolBarItemStyle.Separator ) text = "-"; // If this is a combobox if ( item.ComboBox != null ) { MenuCommands.Add( new MenuCommand( item.ComboBox ) ); item.ComboBox.Visible = true; // I know where this combobox comes from ComboBoxBase cbb = ( ComboBoxBase )item.ComboBox; cbb.ToolBarUse = false; continue; } Bitmap bmp = ( item.Image != null ) ? (Bitmap)item.Image : ( ( item.ImageIndex > -1 ) ? (Bitmap)item.ToolBar.ImageList.Images[ item.ImageIndex ] : null ); EventHandler hndl = new EventHandler( RaiseItemClick ); MenuCommand currentMenuCommand = new MenuCommand( text, bmp, (Shortcut)item.Shortcut, hndl, item ); currentMenuCommand.Checked = item.Checked; currentMenuCommand.Enabled = item.Enabled; MenuCommands.Add(currentMenuCommand); // If we have a menubar if ( item.MenuItems != null) AddSubMenu(currentMenuCommand, item.MenuItems); } }
public void Remove(MenuCommand value) { // Use base class to process actual collection operation base.List.Remove(value as object); }
protected void OnPopupStart(MenuCommand mc) { if (PopupStart != null) PopupStart(mc); }
public PopupMenu() { // Create collection objects drawCommands = new ArrayList(); menuCommands = new MenuCommandCollection(); // Default the properties returnDir = 0; extraSize = 0; popupItem = -1; trackItem = -1; childMenu = null; exitLoop = false; popupDown = true; mouseOver = false; grabFocus = false; excludeTop = true; popupRight = true; parentMenu = null; excludeOffset = 0; focusCatcher = null; parentControl = null; returnCommand = null; oldFocus = IntPtr.Zero; showInfrequent = false; style = VisualStyle.IDE; lastMousePos = new Point(-1,-1); direction = Direction.Horizontal; textFont = SystemInformation.MenuFont; // Create and initialise the timer object (but do not start it running!) timer = new Timer(); timer.Interval = selectionDelay; timer.Tick += new EventHandler(OnTimerExpire); }
protected void OnCommandChanged(MenuCommand item, MenuCommand.Property prop) { Recalculate(); Invalidate(); }
protected void ProcessKeyLeft() { if ( trackItem != -1) { // Get the col this item is in DrawCommand dc = drawCommands[ trackItem] as DrawCommand; // Grab the current column/row values int col = dc.Col; int row = dc.Row; // If not in the first column then move left one if (col > 0) { int newItem = -1; int newRow = -1; int findCol = col - 1; DrawCommand newDc = null; for(int i=0; i< drawCommands.Count; i++) { DrawCommand listDc = drawCommands[i] as DrawCommand; // Interesting in cells in the required column if (listDc.Col == findCol) { // Is this Row nearer to the one required than those found so far? if ((listDc.Row <= row) && (listDc.Row > newRow) && !listDc.Separator && listDc.Enabled) { // Remember this item newRow = listDc.Row; newDc = listDc; newItem = i; } } } if (newDc != null) { // Track the new item // Modify the display of the two items SwitchSelection( trackItem, newItem, false, false); return; } } // Are we the first submenu of a parent control? bool autoLeft = ( parentMenu == null) && ( parentControl != null); // Do we have a parent menu? if (( parentMenu != null) || autoLeft) { // Tell the parent on return that nothing was selected returnCommand = null; // Finish processing messages timer.Stop(); exitLoop = true; if (autoLeft) returnDir = -1; } } }
public MenuControl() { // Set default values this.Dock = DockStyle.Top; _trackItem = -1; _selected = false; _multiLine = false; _popupMenu = null; _mouseOver = false; _manualFocus = false; _drawUpwards = false; _plainAsBlock = false; _oldFocus = IntPtr.Zero; _ignoreEscapeUp = false; _ignoreMouseMove = false; _dismissTransfer = false; _style = VisualStyle.IDE; _chevronStartCommand = null; _direction = Direction.Horizontal; _menuCommands = new MenuCommandCollection(); // Prevent flicker with double buffering and all painting inside WM_PAINT SetStyle(ControlStyles.DoubleBuffer, true); SetStyle(ControlStyles.AllPaintingInWmPaint, true); // Should not be allowed to select this control SetStyle(ControlStyles.Selectable, false); // Hookup to collection events _menuCommands.Cleared += new CollectionClear( OnCollectionCleared ); _menuCommands.Inserted+= new CollectionChange( OnCollectionInserted ); _menuCommands.Removed += new CollectionChange( OnCollectionRemoved ); // Set the default menu color as background this.BackColor = SystemColors.Control; // Do not allow tab key to select this control this.TabStop = false; // Default the Font we use this.Font = SystemInformation.MenuFont; // Calculate the initial height/width of the control _rowWidth = _rowHeight = this.Font.Height + _breadthGap * 2 + 1; // Default to one line of items this.Height = _rowHeight; // Add ourself to the application filtering list Application.AddMessageFilter(this); }
protected bool ProcessKeyRight() { // Are we the first submenu of a parent control? bool autoRight = ( parentControl != null); bool checkKeys = false; bool ret = false; // Is an item currently selected? if ( trackItem != -1) { DrawCommand dc = drawCommands[ trackItem] as DrawCommand; // Does this item have a submenu? if (dc.SubMenu) { // Consume the keyboard message to prevent the submenu immediately // processing the same message again. Remember this routine is called // after PeekMessage but the message is still on the queue at this point Win32.MSG msg = new Win32.MSG(); WindowsAPI.GetMessage(ref msg, 0, 0, 0); // Handle the submenu OperateSubMenu( trackItem, true); ret = true; } else { // Grab the current column/row values int col = dc.Col; int row = dc.Row; // If not in the first column then move left one int newItem = -1; int newRow = -1; int findCol = col + 1; DrawCommand newDc = null; for(int i=0; i< drawCommands.Count; i++) { DrawCommand listDc = drawCommands[i] as DrawCommand; // Interesting in cells in the required column if (listDc.Col == findCol) { // Is this Row nearer to the one required than those found so far? if ((listDc.Row <= row) && (listDc.Row > newRow) && !listDc.Separator && listDc.Enabled) { // Remember this item newRow = listDc.Row; newDc = listDc; newItem = i; } } } if (newDc != null) { // Track the new item // Modify the display of the two items SwitchSelection( trackItem, newItem, false, false); } else checkKeys = true; } } else { if ( parentMenu != null) { if (!ProcessKeyDown()) checkKeys = true; } else checkKeys = true; } // If we have a parent control and nothing to move right into if (autoRight && checkKeys) { returnCommand = null; // Finish processing messages timer.Stop(); exitLoop = true; returnDir = 1; } return ret; }