Pop up menu colour selector (upgraded).
Posted: Mon Jul 17, 2006 12:45 am
Code updated for 5.20+ (same as ColorRequester())
The following little library allows the developer to easily create a popup menu with a selection of colour cells to choose from. Such a menu can easily be attached to a drop down toolbar button etc. (like MS Word).
[ img]http://www.rodriguz.freeserve.co.uk/menucolour.jpg[/img]
Include file
Example:
Tested on Win XP only.
Regards.
The following little library allows the developer to easily create a popup menu with a selection of colour cells to choose from. Such a menu can easily be attached to a drop down toolbar button etc. (like MS Word).
[ img]http://www.rodriguz.freeserve.co.uk/menucolour.jpg[/img]
Include file
Code: Select all
;Menu Colour picker .
;Stephen Rodriguez 2006.
;Purebasic 4 for Windows.
;This small 'include' file allows the developer to show a pop-up menu which contains a selection
;of 'standard colour' cells for the simple selection of colours.
;Now includes the option of adding 12 custom colours by passing an optional address for the first
;element of the array of colours.
;Such a menu can easily be attached to a drop down toolbar button.
EnableExplicit
Declare.l PopupColorMenu(hWnd, customarray=0, x=-1,y=-1)
Declare.l MenuColour_MouseProc(nCode, wParam, lParam)
;The following enumeration lists the two menu items which we use.
;You must ensure that these do not clash with any in your program.
Enumeration
#MCP_colours=100
#MCP_morecolours
EndEnumeration
;The following structure wraps up the global variables required and some used to
;avoid repetetive calculations when drawing colour cells etc.
Structure _MC
oldproc.l
menuhwnd.l
hook.l
selecteditem.l
blackpen.l
whitepen.l
menuwindowhwnd.l
customcolors.l ;Optional pointer to an array(11) of custom colours.
;Used for calculations.
leftbase.l ;x-coord of left most colour cell.
topbase.l ;y-coord of left most colour cell.
EndStructure
Global _mcg._MC
#MC_colorwidth=14 ;The width of each colour cell.
;The following procedure takes responsibility for drawing the colour cells.
Procedure MenuColour_CallbackProc(hWnd, uMsg, wParam, lParam)
Protected result, *lpmis.MEASUREITEMSTRUCT, *lpdis.DRAWITEMSTRUCT
Protected hdc, brush, oldbrush, borderpen, oldpen
Protected col, row, color
Select uMsg
Case #WM_COMMAND
;Check if the user selected the 'More colours...' option.
If wParam>>16 & $ffff=0 And wParam&$ffff=#MCP_morecolours
_mcg\selecteditem=ColorRequester()
EndIf
Case #WM_MEASUREITEM ;This message is called when the menu / submenu is first created and is used to size the menu items.
*lpmis = lParam
If *lpmis\CtlType = #ODT_MENU ;Don't want this to run for an owner drawn button etc!
Select *lpmis\itemid
Case #MCP_colours ;The item with the colours.
*lpmis\itemWidth = 120 ;Width of menu.
If _mcg\customcolors
*lpmis\itemHeight = 30+51*#MC_colorwidth/4 ;Height of menuitem with the colour cells.
Else
*lpmis\itemHeight = 30+6*#MC_colorwidth ;Height of menuitem with the colour cells.
EndIf
Case #MCP_morecolours
*lpmis\itemWidth = 120 ;Width of menu.
*lpmis\itemHeight = 25 ;Height of menuitem with the colour cells.
EndSelect
result = #True
EndIf
Case #WM_DRAWITEM
*lpdis = lParam
If *lpdis\CtlType = #ODT_MENU ;Don't want this to run for an owner drawn button etc!
hdc = *lpdis\hDC
SetBkMode_(hdc,#TRANSPARENT)
Select *lpdis\itemid
Case #MCP_colours ;The item with the colours.
_mcg\leftbase = (*lpdis\rcItem\right-17*#MC_colorwidth/2)/2
_mcg\topbase = 30
*lpdis\rcItem\left+10 : *lpdis\rcItem\top+5
DrawText_(hdc, @"Standard colors:", 16, *lpdis\rcItem,#DT_SINGLELINE|#DT_LEFT|#DT_TOP)
*lpdis\rcItem\left-10 : *lpdis\rcItem\top-5
For row = 0 To 3
For col = 0 To 5
brush=CreateSolidBrush_(RGB(row/2*$ff,col*$33,$ff*row%2))
oldbrush=SelectObject_(hdc,brush)
Rectangle_(hdc, _mcg\leftbase+col*3*#MC_colorwidth/2, _mcg\topbase+row*3*#MC_colorwidth/2, _mcg\leftbase+col*3*#MC_colorwidth/2+#MC_colorwidth-1,_mcg\topbase+row*3*#MC_colorwidth/2+#MC_colorwidth-1)
SelectObject_(hdc,oldbrush)
DeleteObject_(brush)
Next
Next
If _mcg\customcolors
*lpdis\rcItem\left+10 : *lpdis\rcItem\top=_mcg\topbase+27*#MC_colorwidth/4
MoveToEx_(hdc,_mcg\leftbase, *lpdis\rcItem\top,0)
LineTo_(hdc, _mcg\leftbase+17*#MC_colorwidth/2,*lpdis\rcItem\top)
*lpdis\rcItem\top=_mcg\topbase+15*#MC_colorwidth/2
DrawText_(hdc, @"Custom colors:", 14, *lpdis\rcItem,#DT_SINGLELINE|#DT_LEFT|#DT_TOP)
For row = 6 To 7
For col = 0 To 5
color=PeekL(_mcg\customcolors+SizeOf(long)*((row-6)*6+col))
If color = -1 : color = 0 : EndIf ;Show undefined colours in black.
brush=CreateSolidBrush_(color)
oldbrush=SelectObject_(hdc,brush)
Rectangle_(hdc, _mcg\leftbase+col*3*#MC_colorwidth/2, _mcg\topbase+row*3*#MC_colorwidth/2, _mcg\leftbase+col*3*#MC_colorwidth/2+#MC_colorwidth-1,_mcg\topbase+row*3*#MC_colorwidth/2+#MC_colorwidth-1)
SelectObject_(hdc,oldbrush)
DeleteObject_(brush)
Next
Next
EndIf
Case #MCP_morecolours ;The 'more colours' item.
SetBkMode_(hdc,#TRANSPARENT)
If *lpdis\itemState & #ODS_SELECTED
borderpen=CreatePen_(#PS_SOLID, 1, 0)
oldpen=SelectObject_(hdc,borderpen)
Rectangle_(hdc, *lpdis\rcItem\left, *lpdis\rcItem\top, *lpdis\rcItem\right, *lpdis\rcItem\bottom)
Else
borderpen=GetStockObject_(#NULL_PEN)
oldpen=SelectObject_(hdc,borderpen)
Rectangle_(hdc, *lpdis\rcItem\left, *lpdis\rcItem\top, *lpdis\rcItem\right+1, *lpdis\rcItem\bottom+1)
EndIf
SelectObject_(hdc,oldpen)
DeleteObject_(borderpen)
*lpdis\rcItem\left+10
DrawText_(hdc, @"More colors...", 14, *lpdis\rcItem,#DT_SINGLELINE|#DT_LEFT|#DT_VCENTER)
EndSelect
result = #True
EndIf
Default
If _mcg\oldproc : ProcedureReturn CallWindowProc_(_mcg\oldproc, hWnd, uMsg, wParam, lParam) : EndIf
EndSelect
ProcedureReturn result
EndProcedure
;The following hook procedure follows the mouse whilst the MenuColour is visible, displaying selection
;borders as appropriate.
Procedure.l MenuColour_MouseProc(nCode, wParam, lParam)
Protected *mp.MOUSEHOOKSTRUCT, rc.RECT, leftdis, topdis, selecteditem, row, col
Protected hdc, i, j, oldpen, oldbrush, flag, color
If nCode >=0 And wParam = #WM_MOUSEMOVE
selecteditem=-1
*mp =lParam
GetMenuItemRect_(0,_mcg\menuhwnd,0,rc) ;Bounding rectangle of the menucolour item.
;Calculations required for determining the coordinates of individual colour cells.
leftdis=(*mp\pt\x-_mcg\leftbase-rc\left)
topdis=(*mp\pt\y-_mcg\topbase-rc\top)
row=topdis/(3*#MC_colorwidth/2) : col = leftdis/(3*#MC_colorwidth/2)
;Redraw the colour borders in the cases where the cursor is within the colour menu item or
;there is an item left selected because of the speed of the cursor movement.
If PtInRect_(rc,*mp\pt\x | *mp\pt\y << 32) Or _mcg\selecteditem>-1
;Determine if the cursor is over a colour item.
If leftdis%(3*#MC_colorwidth/2)<#MC_colorwidth And leftdis%(3*#MC_colorwidth/2)>=0 And topdis%(3*#MC_colorwidth/2)<#MC_colorwidth And topdis%(3*#MC_colorwidth/2)>=0 And row<8 And col <6
selecteditem= row*6 + col
EndIf
If Not(_mcg\menuwindowhwnd)
_mcg\menuwindowhwnd=WindowFromPoint_(*mp\pt\x | *mp\pt\y << 32)
EndIf
hdc=GetDC_(_mcg\menuwindowhwnd)
If hdc
oldbrush=SelectObject_(hdc,GetStockObject_(#NULL_BRUSH))
oldpen=SelectObject_(hdc, _mcg\whitepen)
For i = 0 To 7
For j = 0 To 5
If selecteditem=i*6+j
If (i = 6 Or i = 7) And _mcg\customcolors ;Custom colors.
color=PeekL(_mcg\customcolors+SizeOf(long)*((i-6)*6+j))
If color<> - 1
_mcg\selecteditem=color
Goto MCP_highlight
Else ;Draw a white border.
Rectangle_(hdc, _mcg\leftbase+j*3*#MC_colorwidth/2-2, _mcg\topbase+i*3*#MC_colorwidth/2-2, _mcg\leftbase+j*3*#MC_colorwidth/2+#MC_colorwidth+2,_mcg\topbase+i*3*#MC_colorwidth/2+#MC_colorwidth+2)
EndIf
Else
_mcg\selecteditem=RGB(row/2*$ff,col*$33,$ff*row%2)
MCP_highlight:
flag=1
SelectObject_(hdc, _mcg\blackpen)
Rectangle_(hdc, _mcg\leftbase+j*3*#MC_colorwidth/2-2, _mcg\topbase+i*3*#MC_colorwidth/2-2, _mcg\leftbase+j*3*#MC_colorwidth/2+#MC_colorwidth+2,_mcg\topbase+i*3*#MC_colorwidth/2+#MC_colorwidth+2)
SelectObject_(hdc,_mcg\whitepen)
EndIf
Else ;Draw a white border.
Rectangle_(hdc, _mcg\leftbase+j*3*#MC_colorwidth/2-2, _mcg\topbase+i*3*#MC_colorwidth/2-2, _mcg\leftbase+j*3*#MC_colorwidth/2+#MC_colorwidth+2,_mcg\topbase+i*3*#MC_colorwidth/2+#MC_colorwidth+2)
EndIf
Next
If i = 3 : i = 5 : EndIf
Next
SelectObject_(hdc, oldpen)
SelectObject_(hdc, oldbrush)
ReleaseDC_(_mcg\menuwindowhwnd, hdc)
If Not(flag)
_mcg\selecteditem=-1
EndIf
EndIf
EndIf
EndIf
ProcedureReturn CallNextHookEx_(_mcg\hook, nCode, wParam, lParam)
EndProcedure
;Display the MenuColourPicker menu.
;Returns the selected colour or -1.
;Note that the optional 'customcolors' points to a colour array of 12 elements.
;e.g. PopupColorMenu(WindowID(0), @colors(0))
;Dim this array by: Dim colors(11).
;Declare undefined colours with a value of -1. See the demo program for details.
Procedure.l PopupColorMenu(hWnd, customcolors=0, x=-1,y=-1)
Protected menu, hInstance, lpdwProcessId
menu=CreatePopupMenu(#PB_Any)
;Set globals.
_mcg\menuhwnd=MenuID(menu)
_mcg\blackpen=CreatePen_(#PS_SOLID, 2, 0)
_mcg\whitepen=CreatePen_(#PS_SOLID, 2, GetSysColor_(#COLOR_MENU))
_mcg\selecteditem =-1
_mcg\menuwindowhwnd=0
_mcg\leftbase=-1
_mcg\topbase=-1
_mcg\customcolors=customcolors
MenuItem(#MCP_colours, "")
ModifyMenu_(_mcg\menuhwnd,#MCP_colours,#MF_BYCOMMAND|#MF_OWNERDRAW,#MCP_colours,_mcg\menuhwnd)
MenuBar()
MenuItem(#MCP_morecolours, "")
ModifyMenu_(_mcg\menuhwnd,#MCP_morecolours,#MF_BYCOMMAND|#MF_OWNERDRAW,#MCP_morecolours,_mcg\menuhwnd)
_mcg\oldproc=SetWindowLong_(hWnd, #GWL_WNDPROC, @MenuColour_CallbackProc())
hInstance = GetModuleHandle_(0)
lpdwProcessId = GetWindowThreadProcessId_(hWnd, 0)
_mcg\hook= SetWindowsHookEx_(#WH_MOUSE, @MenuColour_MouseProc(), hInstance, lpdwProcessId)
If x=-1 Or y = -1
DisplayPopupMenu(menu, hWnd)
Else
DisplayPopupMenu(menu, hWnd,x,y)
EndIf
FreeMenu(menu)
DeleteObject_(_mcg\blackpen)
DeleteObject_(_mcg\whitepen)
UnhookWindowsHookEx_(_mcg\hook)
WindowEvent() ;Just in case the 'More colors...' option was clicked.
SetWindowLong_(hWnd, #GWL_WNDPROC, _mcg\oldproc)
ProcedureReturn _mcg\selecteditem
EndProcedure
Code: Select all
XIncludeFile "MenuColourPicker.pbi"
Define Quit, i
;Define a color array. This is optional and consists of 12 elements, not all of which have to be defined.
Dim colors(11)
;In this example we define 4 custom colors and mark the remaining 8 as undefined by setting them to -1.
;(The user will not be able to select these undefined colours.)
colors(0)=#Green
colors(1)=#Blue
colors(2)=#Gray
colors(3)=#Red
For i = 4 To 11 : colors(i)=-1 : Next
If OpenWindow(0, 200, 200, 400, 400, "MenuColourPicker Example")
Repeat
Select WaitWindowEvent() ; check for window events
Case #WM_RBUTTONDOWN ; right mouse button was clicked =>
CompilerIf #PB_Compiler_Debugger = 1
Debug "Selected colour = " + Str(PopupColorMenu(WindowID(0),@colors(0)))
CompilerElse
MessageRequester("Selected colour", Str(PopupColorMenu(WindowID(0),@colors(0))))
CompilerEndIf
Case #PB_Event_CloseWindow
Quit = 1
EndSelect
Until Quit = 1
EndIf
Regards.