* Program..: Menu.prg * Author...: Jeremy Suiter * Date.....: 19/03/2003 * * Notice...: Copyright (c) 2003, Jeremy Suiter, All Rights Reserved * Notes....: Visual DbEditor menu functions based on ASTAG sample #Include "Gra.ch" #Include "Xbp.ch" #Include "Appevent.ch" #Include "DbEditor.ch" #Include "Dll.ch" #Include "Font.ch" #Include "FoxDbe.ch" #Include "CdxDbe.ch" #Include "Dmlb.ch" /* * Wrapper function to get the WindowMenu object without a reference stored * in a variable. */ FUNCTION WinMenu Return MainWindow():childFromName(WindowMenu():ID) /* * When a XbpDialog window is created as child, this procedure adds the * window title to the window menu */ PROCEDURE WinMenuAdd(oDlg) LOCAL oWinMenu:=WinMenu() If oWinMenu<>NIL oDlg:setDisplayFocus:={|mp1,mp2,obj| oWinMenu:checkItem(obj) } oWinMenu:addItem(oDlg) EndIf Return /* * When a child window is closed, this procedure removes its title from * the window menu */ PROCEDURE WinMenuDel(oDlg) LOCAL oWinMenu:=WinMenu() If oWinMenu<>NIL oDlg:setDisplayFocus:=NIL oWinMenu:delItem(oDlg) EndIf Return /* * Disable a window and its corresponding menu item. */ PROCEDURE WinMenuDisable(oDlg) LOCAL oWinMenu:=WinMenu() If oWinMenu<>NIL oWinMenu:disableItem(oDlg) EndIf Return /* * Enables a dialog window together with the corresponding menu item */ PROCEDURE WinMenuEnable(oDlg) LOCAL oWinMenu:=WinMenu() If oWinMenu<>NIL oWinMenu:enableItem(oDlg) EndIf Return /* * User-defined Menu class to manage child windows in an MDI application */ CLASS WindowMenu FROM XbpMenu PROTECTED: EXPORTED: CLASS METHOD initClass CLASS VAR Id READONLY VAR windowStack, winCount VAR menuPos VAR currentWin , offset READONLY METHOD init, create, addItem , insItem , setItem, delItem METHOD setChildWin , closeWin , closeAll, nextWinPos METHOD enableItem , disableItem, checkItem ENDCLASS /* * Initialize class object * * The :Id is used to retrieve a WindowMenu object from an MDI window * using the :childFromName() method -> see function WinMenu() above */ CLASS METHOD WindowMenu:initClass ::id:=123456789 Return self /* * Initialize object */ METHOD WindowMenu:init(oParent, aPresParam, lVisible) ::xbpMenu:init(oParent, aPresParam, lVisible) ::windowStack:={} ::winCount :=0 ::menuPos :=0 ::offset :=0 ::title :="~Window" Return self /* * Request system resources and add default menu items */ METHOD WindowMenu:create(oParent, aPresParam, lVisible) ::xbpMenu:create(oParent, aPresParam, lVisible) ::xbpMenu:addItem({ MENU_CASCADE , {|| MainWindow():cascade() } }) ::xbpMenu:addItem({ MENU_CLOSE , {|| ::closeWin() } }) ::xbpMenu:addItem({ MENU_CLOSEALL, {|| ::closeAll() } }) ::xbpMenu:addItem(MENUITEM_SEPARATOR) ::xbpMenu:setName(::Id) ::offset :=::xbpMenu:numItems() ::itemSelected:={|nItem| ::setChildWin(nItem - ::offset) } Return self /* * Calculate position for next window to open from position of * ::currentWin and size of new window */ METHOD WindowMenu:nextWinPos(aSize) LOCAL oDialog:=::setParent():setParent() LOCAL aDaSize:=oDialog:drawingArea:currentSize() LOCAL aPos, aPosTmp LOCAL i If ::numItems() == ::offset /* * No window is open */ aPos:={ 0, aDaSize[2] - aSize[2] } ElseIf ::currentWin:getFrameState() == XBPDLG_FRAMESTAT_MINIMIZED /* * The current window is currently minimized; try to find * another window for computing the window position */ aPos:={ 0, aDaSize[2] } FOR i:=1 TO Len(::windowStack) If ::windowStack[i]:getFrameState() != XBPDLG_FRAMESTAT_MINIMIZED aPosTmp :=::windowStack[i]:currentPos() aPosTmp[2]+= ::windowStack[i]:currentSize()[2] If aPosTmp[1] > aPos[1] .OR. aPosTmp[2] < aPos[2] aPos:=aPosTmp EndIf EndIf NEXT aPos:={ aPos[1] + 24, aPos[2] - aSize[2] - 24 } Else aPos:=::currentWin:currentPos() aPos:={ aPos[1] + 24, ; aPos[2] + ::currentWin:currentSize()[2] - aSize[2] - 24 } EndIf Return aPos /* * Use title of child window as text for menu item */ METHOD WindowMenu:addItem(oDlg) LOCAL oOwner:=oDlg:setOwner() LOCAL oParent:=oDlg:setParent() LOCAL i, cItem If oOwner<>oParent .And. ; (i:=AScan(::windowStack, oOwner)) < Len(::windowStack) /* * Owned window must be adjacent to owner in the menu */ ::insItem(i+1, oDlg) Else AAdd(::windowStack, oDlg) ::winCount ++ cItem:="~" + Ltrim(Str(::winCount)) + " " + oDlg:getTitle() ::xbpMenu:addItem({ cItem, NIL}) If ::numItems() == ::offset + 1 /* * The first window managed by the WindowMenu opens * Display WindowMenu in menubar */ If ::setParent():numItems() < ::menuPos ::setParent():addItem({self, NIL}) ::menuPos:=::setParent():numItems() Else ::setParent():insItem(::menuPos, {self, NIL}) EndIf EndIf ::setChildWin(::winCount) EndIf Return self /* * Insert a window to window stack and menu */ METHOD WindowMenu:insItem(nStackPos, oDlg) LOCAL cItem:=oDlg:getTitle() AAdd(::windowStack, NIL) AIns(::windowStack, nStackPos) ::windowStack[ nStackPos ]:=oDlg ::winCount ++ cItem:="~" + Ltrim(Str(nStackPos)) + " " + cItem ::xbpMenu:insItem(nStackPos + ::offset, {cItem, NIL}) AEval(::windowStack, {|oWin| ::setItem(oWin) }, nStackPos + 1) ::setChildWin(nStackPos) Return self /* * Disable child window and menu item */ METHOD WindowMenu:disableItem(oDlg) LOCAL i:=AScan(::windowStack, oDlg) If i > 0 ::xbpMenu:disableItem(i + ::offset) EndIf oDlg:disable() Return self /* * Enable child window and menu item */ METHOD WindowMenu:enableItem(oDlg) LOCAL i:=AScan(::windowStack, oDlg) If i > 0 ::xbpMenu:enableItem(i + ::offset) EndIf oDlg:enable() Return self /* * Transfer changed window title to menu item */ METHOD WindowMenu:setItem(oDlg) LOCAL aItem, i:=AScan(::windowStack, oDlg) If i == 0 ::addItem(oDlg) Else aItem :=::xbpMenu:getItem(i + ::offset) aItem[1] :="~" + Ltrim(Str(i)) + " " + oDlg:getTitle() ::xbpMenu:setItem(i + ::offset, aItem) /* * Disabled and Checked status is lost for menu item. Reset! */ If .NOT. oDlg:isEnabled() ::xbpMenu:disableItem(i + ::offset) EndIf If oDlg == ::currentWin ::checkItem(oDlg) EndIf EndIf Return self /* * Delete child window from window stack and from menu */ METHOD WindowMenu:delItem(oDlg) LOCAL i :=AScan(::windowStack, oDlg) If i > 0 .And. ::status() == XBP_STAT_CREATE ::xbpMenu:delItem(i + ::offset) If ::currentWin == ::windowStack[i] ::currentWin:=NIL EndIf ADel(::windowStack, i) ::winCount -- Asize(::windowStack, ::winCount) If i <= ::winCount /* * re-number menu items below the deleted item */ AEval(::windowStack, {|o| ::setItem(o) }, i) EndIf If ::winCount == 0 /* * The last child window is closed. * Remove Window Menu from menubar */ ::currentWin:=NIL ::setParent():delItem(::menuPos) ElseIf ::currentWin == NIL ::setChildWin(Min(i, ::winCount)) EndIf EndIf Return self /* * Check the corresponding menu item of a child window when it gets focus. * This method must be called from the :setDisplayFocus callback of * a child window -> see procedure WinMenuAdd() above */ METHOD WindowMenu:checkItem(oDlg) LOCAL i If ::currentWin<>oDlg .And. ::status() == XBP_STAT_CREATE If ::currentWin<>NIL i:=AScan(::windowStack, ::currentWin) + ::offset ::xbpMenu:checkItem(i , .F.) EndIf i:=AScan(::windowStack, oDlg) + ::offset ::xbpMenu:checkItem(i, .T.) ::currentWin:=oDlg EndIf Return self /* * Set focus to a child window by ordinal position of the window. * This method is called when a window is selected within the window menu */ METHOD WindowMenu:setChildWin(nStackPos) ::checkItem(::windowStack[ nStackPos ]) If ::currentWin:getFrameState() == XBPDLG_FRAMESTAT_MINIMIZED ::currentWin:setFrameState(XBPDLG_FRAMESTAT_NORMALIZED) EndIf SetAppFocus(::currentWin) Return self /* * Close current child window * * Note: The closing routine must be called from the :close callback of a * child window -> see procedure CreateChildWindow() above */ METHOD WindowMenu:closeWin If ::currentWin<>NIL ::currentWin:handleEvent(xbeP_Close, NIL, NIL) EndIf Return self /* * Close all child windows * * Note: The closing routine must be called from the :close callback of a * child window -> see procedure CreateChildWindow() above. * * !!! Otherwise we have an endless loop here !!! */ METHOD WindowMenu:closeAll DO WHILE ! Empty(::windowStack) ATail(::windowStack):handleEvent(xbeP_Close, NIL, NIL) ENDDO Return self