Saturday, September 02, 2006

Updated OwnerDrawn menu class

Today, the OwnerDrawn menu class has been updated (the article was published on UT Magazine October 2005 edition). A few bug has been fixed. And few features has been added based on e-mail I received lately.

I hope you enjoy the new class,
happy coding!

Download
OwnerDrawn Menus class & sample

10 comments:

Anonymous said...

Thanks Hermam, I will look at it.

Anonymous said...

Hello Hermman.



Happy New Year!

Your OwnerDrawn menu class is excellent!

I only see one drawback: You have to write too much code to define your menu.

I tried to make the life of developers easier by giving your class the ability to read and execute a standard VFP Menu (MPX) but I couldn’t because I don’t understand your code, I’m not that good ;).

I only could find some code to extract the contents of a MNX table into a treeview. Maybe with this you would be able to do it.

The code is attached; please give this request a try, in the MNX you could find the Prompt name, the accelerator key, the picture and the code to execute when the menu bar is pressed.

Thanks in advance.

Best regards:

Victor Chigne


(Code below)

menu2tree.prg
lparameters tcMenuAlias, toTreeControl

if pcount() < 1
messagebox("No Menu Table or no tree view object reference has been passed")
tcMenuAlias = GETFILE("mnx","Open a menu..")
IF EMPTY(tcMenuAlias)
return .f.
ENDIF
ENDIF
lOnluMenu=.F.
IF PCOUNT()=1
* only convert menu
lOnlyMenu = .T.
ENDIF
LOCAL lcMenuBarName, ;
lcWorkingKey, ;
lcKey, ;
lnPads

lcMenuBarName = SPACE(0)
lcWorkingKey = SPACE(0)
lcKey = SPACE(0)

DIMENSION laNode[1,2]

if used('mainmenu')
use in mainmenu
endif
use (tcMenuAlias) in 0 alias mainmenu again shared noupdate

SELECT mainmenu
LOCATE

* No need to work with the default header record of the menu.
SKIP 1

WITH toTreeControl
* Clear previous menu if one existed.
.Nodes.Clear()

lcMenuBarName = mainmenu.LevelName
lnPads = mainmenu.NumItems

laNode[1,1] = lcMenuBarName
laNode[1,2] = .NULL.

SELECT *, RECNO() as nRecno ;
FROM mainmenu ;
WHERE LevelName = lcMenuBarName ;
INTO CURSOR curTreeRootNodes

* Build root
SCAN
IF VAL(curTreeRootNodes.ItemNum) # 0
lcKey = "M" + PADL(TRANSFORM(curTreeRootNodes.nRecNo), 6, "0")
.Nodes.Add(, 1, lcKey, iif(curTreeRootNodes.Prompt='\-','------',strtran(curTreeRootNodes.Prompt,'\<','')))
ENDIF
ENDSCAN

SELECT curTreeRootNodes
PopulateTreeChildNodes("curTreeRootNodes")
ENDWITH

USE IN (SELECT("curTreeRootNodes"))

SELECT MainMenu
LOCATE

return

********************************************************
function PopulateTreeChildNodes

LPARAMETERS tcCursor

SELECT (tcCursor)

SCAN
lcItemNum = &tcCursor..ItemNum
IF VAL(lcItemNum) = 0
LOOP
ENDIF

lnRecordNo = &tcCursor..nRecno
GO lnRecordNo IN MainMenu
SKIP 1 IN MainMenu

lcLevelName = MainMenu.LevelName
lcLevelCursor = "curNodes" + ALLTRIM(lcLevelName)

SELECT *, RECNO() as nRecno ;
FROM mainmenu ;
WHERE LevelName == lcLevelName ;
INTO CURSOR (lcLevelCursor) NOFILTER

SELECT (lcLevelCursor)
SCAN
IF VAL(ItemNum) # 0
lcParentNodeKey = "M" + PADL(TRANSFORM(&tcCursor..nRecno), 6, "0")
lcKey = "M" + PADL(TRANSFORM(&lcLevelCursor..nRecno), 6, "0")
.Nodes.Add(lcParentNodeKey, 4, lcKey, iif(&lcLevelCursor..Prompt='\-','------',strtran(&lcLevelCursor..Prompt,'\<','')))
ENDIF
ENDSCAN

* Select all options at current level that are submenus
lcLevelCursorSub = lcLevelCursor + "subonly"
SELECT * ;
FROM (lcLevelCursor) ;
WHERE ObjCode = 77 ;
INTO CURSOR (lcLevelCursorSub) NOFILTER

IF _tally > 0
PopulateTreeChildNodes(lcLevelCursorSub)
ENDIF

USE IN (SELECT(lcLevelCursorSub))
ENDSCAN

*USE IN (SELECT(lcLevelCursor))

return

Anonymous said...

Hi Herman,

You might have a look at this sample, created by Rafael Lippert. Basically, he tried to d the same as you do, starting from the MNX file to create his menu. This way, people could continue using their own original menus, with no code changes.
Since he saw your ownerdrawn menus, he stopped developing it. Maybe you can get some ideas from there !
Please download directly from:
http://www.lipsil.com.br/arquivos/menu_26_10_2006.zip

At this link you can see some screenshots:
http://www.lipsil.com.br/menu.htm

Regards

Cesar Chalom

Herman Tan said...

Hi Cesar,

I've been asked to do the same thing by others too. You can see the comment from Victor's :)

Now that's what I want to do. I'm sorry I had to postponed that for some time. I think it's time for me to finish this.

Thanks for the link to Rafael's page. Look's good, I'll try it soon.

Thank you for comments & feedback. Appreciate it!

Regards
Herman Tan

Anonymous said...

Hi Herman,

Your OwnerDrawn menu is exellent work. Thank you for sharing it with VFP community.

A question:
I was trying frm_popupmenu.scx sample, and ACTIVATEPOPUP method. It seems will always activate "main" popup. How can I activate a subpopup along with "main" popup ?

Something like native ACTIVATE POPUP xxx BAR yyy, which also shows whole structure above bar specified.

Best regards,

Kruno Malovic
Croatia, Europe

Herman Tan said...

Hi Kruno,

Thanks for your feedback!

Yes it always activate the first (main) popup. Sorry, at this moment the class does not support the activation on specific popup or on specific bar. However, I'll put this on my todo list.

Best regards,
Herman Tan

Anonymous said...

Perhaps you could clarify for me how to use the class with the VFP special menu items like Edit,Cut, Paste, etc. I am using the class fine with other things. What I do not get is how to make them enable/disable automaticakky like they normally would.

Thanks

Herman Tan said...

If you talking about Shortcut popup, then you have to disable the item manually. For instance, put this code in an EditBox.RightClick() event:

Declare Long IsClipboardFormatAvailable in User32 Long nFormat
With oPopup
.CreatePopupItem( 3 )
.aPopupItem[1] = 'Cu&t'
.aPopupItem[2] = '&Copy'
.aPopupItem[3] = '&Paste'
hPopup = .CreatePopup( 1 )
.nFirstId = 1

** Disable Cut & Copy Item
If (This.SelLength == 0)
.DisablePopupItem( 1, 1 )
.DisablePopupItem( 1, 2 )
endif

** Disable Paste Item
If (IsClipboardFormatAvailable(1) == 0)
.DisablePopupItem( 1, 3 )
endif

nResult = .ActivatePopup()
** Do the rest
endif
EndWith

HTH
Herman

vcwalet said...

pak Herman

kl untuk nampilin di menu gambar dari pictres bukan bmp gmana?..

Herman Tan said...

Halo vcwalet,

PictRes memang tidak di support oleh class ini. Hal ini juga sudah disebutkan pada Form OD Menu Generator (OD_GenMenu). Silahkan di baca pada EditBox nya tentang batasan-batasan yg mungkin belum/perlu anda ketahui.