mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-17 06:06:00 -06:00
770 lines
20 KiB
C++
770 lines
20 KiB
C++
#ifndef _WithXMLMenu_h_
|
|
#define _WithXMLMenu_h_
|
|
|
|
#include "XMLMenuInterface.h"
|
|
#include "XMLMenuEditor.h"
|
|
|
|
NAMESPACE_UPP
|
|
|
|
// main XML menu template
|
|
template<class T> class WithXMLMenu : public T, public XMLMenuInterface
|
|
{
|
|
private:
|
|
|
|
// the available commands
|
|
XMLCommands commands;
|
|
|
|
// the configurable toolbars
|
|
XMLToolBars toolBars;
|
|
|
|
// the configurable menu bars
|
|
// the bar named 'Main' is the main menu
|
|
// others are context menus
|
|
XMLToolBars menuBars;
|
|
|
|
// the four corner XMLToolbarFrame frames
|
|
XMLToolBarFrame topFrame;
|
|
XMLToolBarFrame bottomFrame;
|
|
XMLToolBarFrame leftFrame;
|
|
XMLToolBarFrame rightFrame;
|
|
|
|
// the menu bars
|
|
ArrayMap<String, MenuBar> menuBarCtrls;
|
|
|
|
// toolbar controls storage area
|
|
ArrayMap<String, XMLToolBarCtrl> toolBarCtrls;
|
|
|
|
// flags stating allowed dock point and main menu bar
|
|
bool dockTop, dockBottom, dockLeft, dockRight;
|
|
bool mainMenu;
|
|
|
|
// refrash frames when menu/toolbars changed
|
|
// or docking points changed
|
|
void RefreshFrames(void);
|
|
|
|
// dragging flag
|
|
bool dragging;
|
|
|
|
// toolbar being dragged
|
|
Ptr<XMLToolBarCtrl> dragToolBar;
|
|
|
|
// pre-docking stuffs
|
|
Ptr<XMLToolBarFrame> preDockFrame;
|
|
|
|
// menu and toolbars setter callbacks
|
|
void SetMenuBar0(Bar &bar, int mnIdx, Array<XMLToolBarItem> const *items);
|
|
void SetMenuBar(Bar &bar, int mnIdx) { SetMenuBar0(bar, mnIdx, NULL); }
|
|
|
|
void SetToolBar0(Bar &bar, int tbIdx, Array<XMLToolBarItem> const *items);
|
|
void SetToolBar(Bar &bar, int tbIdx) { SetToolBar0(bar, tbIdx, NULL); }
|
|
|
|
// calls user command handler, passing command ID to it
|
|
void callUserHandler(String const &s) { UserCmdHandler(s); }
|
|
|
|
// docks/undocks/hide a toolbar
|
|
WithXMLMenu<T> &Reposition(XMLToolBarCtrl *tb, XMLToolBarState state, Point p);
|
|
|
|
// query a dock frame under screen point
|
|
XMLToolBarFrame *QueryDockFrame(Point p);
|
|
|
|
// frame mouse event, captures right click on docking frames
|
|
// allowing to activate 'customize' even without any toolbar displayed
|
|
Image FrameMouseEvent(int event, Point p, int zdelta, dword keyflags);
|
|
|
|
// child frame mouse event handler captures the click on toolbar's frames
|
|
virtual void ChildFrameMouseEvent(Ctrl *child, int event, Point p, int zdelta, dword keyflags);
|
|
|
|
// mouse events sent from floating toolbar
|
|
virtual void FloatingDraggedEvent(XMLToolBarCtrl &tb, Point p);
|
|
|
|
// run the menu editor
|
|
void runEditorCb(void) { XMLMenuEditor(this).RunAppModal(); }
|
|
|
|
// toggles bar visibility
|
|
void toggleBarCb(int iBar);
|
|
|
|
// right click context menu
|
|
void XMLContextMenu(Bar& bar);
|
|
|
|
// right click event sent from various controls
|
|
virtual void RightClickEvent(Point p) { MenuBar::Execute(THISBACK(XMLContextMenu)); }
|
|
|
|
// the drag loop
|
|
void DragLoop(Point startP);
|
|
|
|
// refresh menus and bars
|
|
void RefreshBarsDeep(void);
|
|
void RefreshBars(void);
|
|
|
|
// sync bars from ctrls
|
|
void SyncBars(void);
|
|
|
|
public:
|
|
|
|
typedef WithXMLMenu<T> CLASSNAME;
|
|
|
|
WithXMLMenu();
|
|
~WithXMLMenu();
|
|
|
|
// user commands handler
|
|
Callback1<String> UserCmdHandler;
|
|
|
|
// sets builtin commands
|
|
void SetCommands(Callback1<XMLCommands &> cmds);
|
|
|
|
// sets menu entries
|
|
void SetMenuBars(Callback1<XMLToolBars &> tb);
|
|
|
|
// sets toolbars entries
|
|
void SetToolBars(Callback1<XMLToolBars &> tb);
|
|
|
|
// gets/sets commands, menu and toolbars
|
|
virtual XMLCommands const &GetCommands(void) { return commands; }
|
|
virtual void SetCommands(XMLCommands &cmds) { commands = pick(cmds); RefreshBars(); }
|
|
virtual XMLToolBars const &GetMenuBars(void) { return menuBars; }
|
|
virtual void SetMenuBars(XMLToolBars &tb) { menuBars = pick(tb); RefreshBars(); }
|
|
virtual XMLToolBars const &GetToolBars(void) { SyncBars(); return toolBars; }
|
|
virtual void SetToolBars(XMLToolBars &tb) { toolBars = pick(tb); RefreshBars(); }
|
|
|
|
// controls docking and main menu behaviour
|
|
WithXMLMenu<T> &DockTop(bool b = true) { dockTop = b; RefreshFrames(); return *this; }
|
|
WithXMLMenu<T> &NoDockTop(void) { return DockTop(false); }
|
|
WithXMLMenu<T> &DockBottom(bool b = true) { dockBottom = b; RefreshFrames(); return *this; }
|
|
WithXMLMenu<T> &NoDockBottom(void) { return DockBottom(false); }
|
|
WithXMLMenu<T> &DockLeft(bool b = true) { dockLeft = b; RefreshFrames(); return *this; }
|
|
WithXMLMenu<T> &NoDockLeft(void) { return DockLeft(false); }
|
|
WithXMLMenu<T> &DockRight(bool b = true) { dockRight = b; RefreshFrames(); return *this; }
|
|
WithXMLMenu<T> &NoDockRight(void) { return DockRight(false); }
|
|
|
|
WithXMLMenu<T> &MainMenu(bool b = true) { mainMenu = b; RefreshFrames(); return *this; }
|
|
WithXMLMenu<T> &NoMainMenu(void) { return MainMenu(false); }
|
|
|
|
// gets a context menu by name -- NULL if none
|
|
MenuBar *GetContextMenu(String const &name);
|
|
|
|
// xml supportbin
|
|
void Xmlize(XmlIO xml);
|
|
};
|
|
|
|
template<class T> WithXMLMenu<T>::WithXMLMenu() :
|
|
topFrame (TOOLBAR_TOP),
|
|
bottomFrame (TOOLBAR_BOTTOM),
|
|
leftFrame (TOOLBAR_LEFT),
|
|
rightFrame (TOOLBAR_RIGHT)
|
|
{
|
|
// allows, by default, docking on 4 corners and embeds
|
|
// main menu too
|
|
dockTop = dockBottom = dockLeft = dockRight = true;
|
|
mainMenu = true;
|
|
|
|
dragging = false;
|
|
dragToolBar = NULL;
|
|
|
|
preDockFrame = NULL;
|
|
|
|
RefreshFrames();
|
|
|
|
}
|
|
|
|
template<class T> WithXMLMenu<T>::~WithXMLMenu()
|
|
{
|
|
}
|
|
|
|
// refrash frames when menu/toolbars changed
|
|
// or docking points changed
|
|
template<class T> void WithXMLMenu<T>::RefreshFrames(void)
|
|
{
|
|
// removes all frames
|
|
T::RemoveFrame(topFrame);
|
|
T::RemoveFrame(bottomFrame);
|
|
T::RemoveFrame(leftFrame);
|
|
T::RemoveFrame(rightFrame);
|
|
|
|
// adds main menu
|
|
int iFrame = 0;
|
|
if(mainMenu)
|
|
{
|
|
int mainIdx = menuBarCtrls.Find("Main");
|
|
if(mainIdx >= 0)
|
|
{
|
|
MenuBar &bar = menuBarCtrls[mainIdx];
|
|
T::RemoveFrame(bar);
|
|
T::InsertFrame(iFrame++, bar);
|
|
}
|
|
}
|
|
|
|
// add docking frames
|
|
if(dockTop)
|
|
T::InsertFrame(iFrame++, topFrame);
|
|
if(dockBottom)
|
|
T::InsertFrame(iFrame++, bottomFrame);
|
|
if(dockLeft)
|
|
T::InsertFrame(iFrame++, leftFrame);
|
|
if(dockRight)
|
|
T::InsertFrame(iFrame, rightFrame);
|
|
}
|
|
|
|
|
|
// docks/undocks/hide a toolbar
|
|
template<class T> WithXMLMenu<T> &WithXMLMenu<T>::Reposition(XMLToolBarCtrl *tb, XMLToolBarState state, Point p)
|
|
{
|
|
switch(state)
|
|
{
|
|
case TOOLBAR_CLOSED :
|
|
tb->Close();
|
|
tb->toolBarPos = p;
|
|
break;
|
|
|
|
case TOOLBAR_FLOATING :
|
|
tb->Float(p);
|
|
break;
|
|
|
|
case TOOLBAR_TOP :
|
|
tb->Dock(topFrame, p);
|
|
break;
|
|
|
|
case TOOLBAR_BOTTOM :
|
|
tb->Dock(bottomFrame, p);
|
|
break;
|
|
|
|
case TOOLBAR_LEFT :
|
|
tb->Dock(leftFrame, p);
|
|
break;
|
|
|
|
case TOOLBAR_RIGHT :
|
|
tb->Dock(rightFrame, p);
|
|
break;
|
|
|
|
default:
|
|
NEVER();
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
// query a dock frame under point
|
|
template<class T> XMLToolBarFrame *WithXMLMenu<T>::QueryDockFrame(Point p)
|
|
{
|
|
// reset point to be on control rect
|
|
if(dockTop && topFrame.Contains(p))
|
|
return &topFrame;
|
|
else if(dockBottom && bottomFrame.Contains(p))
|
|
return &bottomFrame;
|
|
else if(dockLeft && leftFrame.Contains(p))
|
|
return &leftFrame;
|
|
else if(dockRight && rightFrame.Contains(p))
|
|
return &rightFrame;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
// frame mouse event, captures right click on docking frames
|
|
// allowing to activate 'customize' even without any toolbar displayed
|
|
template<class T> Image WithXMLMenu<T>::FrameMouseEvent(int event, Point p, int zdelta, dword keyflags)
|
|
{
|
|
// handles just left down events
|
|
if(event != Ctrl::RIGHTDOWN)
|
|
return T::FrameMouseEvent(event, p, zdelta, keyflags);
|
|
|
|
Ctrl::PostCallback(THISBACK1(RightClickEvent, p));
|
|
return T::FrameMouseEvent(event, p, zdelta, keyflags);
|
|
}
|
|
|
|
// child frame mouse event handler captures the click on toolbar's frames
|
|
template<class T> void WithXMLMenu<T>::ChildFrameMouseEvent(Ctrl *child, int event, Point p, int zdelta, dword keyflags)
|
|
{
|
|
// allow context menu even on child frames
|
|
if(event == Ctrl::RIGHTDOWN)
|
|
{
|
|
Ctrl::PostCallback(THISBACK1(RightClickEvent, p));
|
|
return;
|
|
}
|
|
|
|
// handles just left down events
|
|
if(event != Ctrl::LEFTDOWN)
|
|
return;
|
|
|
|
// ignore re-entrant events
|
|
if(dragging)
|
|
return;
|
|
|
|
// reacts just to events related to XMLToolBar
|
|
dragToolBar = dynamic_cast<XMLToolBarCtrl *>(child);
|
|
if(!dragToolBar)
|
|
return;
|
|
|
|
// setup drag point for dragging the toolbar
|
|
Point dragPoint = p + dragToolBar->GetScreenRect().TopLeft();
|
|
|
|
// execute the drag loop
|
|
DragLoop(dragPoint);
|
|
}
|
|
|
|
// mouse events sent from floating toolbar
|
|
template<class T> void WithXMLMenu<T>::FloatingDraggedEvent(XMLToolBarCtrl &tb, Point p)
|
|
{
|
|
// ignore event if toolbar is not floating
|
|
// should not happen, but.....
|
|
if(!tb.GetIsFloating())
|
|
return;
|
|
|
|
// ignode re-entrant events
|
|
if(dragging)
|
|
return;
|
|
|
|
// setup drag toolbar
|
|
dragToolBar = &tb;
|
|
|
|
// run drag loop
|
|
DragLoop(p);
|
|
}
|
|
|
|
template<class T> void WithXMLMenu<T>::DragLoop(Point dragPoint)
|
|
{
|
|
// marks dragging active to avoid re-entrant events
|
|
dragging = true;
|
|
|
|
// check if dragtoolbar is valid -- interna error if not
|
|
ASSERT(dragToolBar);
|
|
|
|
// ignore mouse events on main control, when dragging
|
|
Ctrl::IgnoreMouse();
|
|
|
|
// popup-ize the toolbar
|
|
dragToolBar->Popup(dragPoint);
|
|
|
|
#if defined(flagX11) || defined(flagGTK)
|
|
// this is needed for X11, otherwise the GetMouseLeft() function
|
|
// may return false even if mouse is down after un-floating
|
|
Sleep(30);
|
|
dragToolBar->SetCapture();
|
|
#endif
|
|
|
|
// loop up to mouse button is released
|
|
Point ps;
|
|
XMLToolBarState state;
|
|
|
|
#ifdef flagGTK
|
|
// GTK doesn't detect that mouse is still pressed, so we must
|
|
// simulate a press event
|
|
Point mousePos = GetMousePos() - Ctrl::GetTopWindow()->GetRect().TopLeft();
|
|
gdk_test_simulate_button(Ctrl::GetTopWindow()->gdk(), mousePos.x, mousePos.y, 1, GDK_BUTTON1_MASK, GDK_BUTTON_PRESS);
|
|
Ctrl::ProcessEvents();
|
|
#endif
|
|
|
|
do
|
|
{
|
|
Ctrl::ProcessEvents();
|
|
ps = GetMousePos();
|
|
Rect r = dragToolBar->GetRect();
|
|
r = r + ps - dragPoint;
|
|
dragPoint = ps;
|
|
dragToolBar->SetRect(r);
|
|
|
|
// query an eventual dock frame under mouse
|
|
if(preDockFrame)
|
|
preDockFrame->UnPreDock(*dragToolBar);
|
|
preDockFrame = QueryDockFrame(ps);
|
|
if(preDockFrame)
|
|
{
|
|
state = preDockFrame->GetToolBarState();
|
|
if(state == TOOLBAR_TOP || state == TOOLBAR_BOTTOM)
|
|
dragToolBar->PopHorz(ps);
|
|
else
|
|
dragToolBar->PopVert(ps);
|
|
preDockFrame->PreDock(*dragToolBar, ps);
|
|
}
|
|
else
|
|
dragToolBar->PopSquare(ps);
|
|
Ctrl::RefreshLayout();
|
|
}
|
|
while(GetMouseLeft());
|
|
|
|
#if defined(flagX11) || defined(flagGTK)
|
|
dragToolBar->ReleaseCapture();
|
|
#endif
|
|
|
|
// if dropped on a frame, dock there
|
|
if(preDockFrame)
|
|
{
|
|
dragToolBar->UnPreDock(*preDockFrame);
|
|
dragToolBar->DockAt(*preDockFrame, ps);
|
|
}
|
|
else
|
|
{
|
|
// otherwise float it
|
|
dragToolBar->Float(ps);
|
|
}
|
|
|
|
// re-accepts mouse events
|
|
Ctrl::NoIgnoreMouse();
|
|
|
|
// end dragging mode
|
|
dragging = false;
|
|
}
|
|
|
|
// menu and toolbars setter callbacks
|
|
template<class T> void WithXMLMenu<T>::SetMenuBar0(Bar &bar, int mnIdx, Array<XMLToolBarItem> const *items)
|
|
{
|
|
// get menu bar items
|
|
if(!items)
|
|
items = &menuBars[mnIdx].GetItems();
|
|
|
|
for(int i = 0; i < items->GetCount(); i++)
|
|
{
|
|
// get current item
|
|
XMLToolBarItem const &item = (*items)[i];
|
|
|
|
// separator handling
|
|
if(item.IsSeparator())
|
|
{
|
|
bar.Separator();
|
|
continue;
|
|
}
|
|
|
|
// submenu handling
|
|
if(item.IsSubMenu())
|
|
{
|
|
bar.Add(item.GetLabel(), item.GetIcon(), THISBACK2(SetMenuBar0, mnIdx, &item.GetSubMenu().GetItems()));
|
|
continue;
|
|
}
|
|
|
|
// get command id
|
|
String id = item.GetId();
|
|
|
|
// check for internal, unnamed commands
|
|
// they have an associated callback and an empty id
|
|
if(id.IsEmpty() && item.GetInternal())
|
|
{
|
|
String label = item.GetLabel();
|
|
Callback handler = item.GetInternal();
|
|
Image const &img = item.GetIcon();
|
|
bar.Add(label, img, handler);
|
|
continue;
|
|
}
|
|
|
|
// get command id and find command
|
|
// if not found, just skip it
|
|
if(!commands.Has(id))
|
|
continue;
|
|
|
|
// get command from id
|
|
XMLCommand &cmd = commands.Get(id);
|
|
|
|
// check wether it's a generated menu "command"
|
|
Callback1<XMLToolBar &> menuCallback = cmd.GetMenuCallback();
|
|
if(menuCallback)
|
|
{
|
|
SetMenuBar0(bar, mnIdx, &cmd.GetMenuTb().GetItems());
|
|
continue;
|
|
}
|
|
|
|
// get callback from command
|
|
bool isCustom = cmd.GetIsCustom();
|
|
Callback handler;
|
|
if(isCustom)
|
|
{
|
|
String cmds = cmd.GetCommandString();
|
|
if(cmds == "")
|
|
cmds = id;
|
|
handler = THISBACK1(callUserHandler, cmds);
|
|
}
|
|
else
|
|
handler = cmd.GetCallback();
|
|
|
|
// get remaining item data
|
|
String label = item.GetLabel();
|
|
if(label == "")
|
|
label = id;
|
|
Image const &img = item.GetIcon();
|
|
|
|
Ctrl *ctrl = cmd.GetCtrl();
|
|
bool enabled = cmd.GetIsEnabled();
|
|
|
|
// adds to menu
|
|
if(ctrl)
|
|
{
|
|
if(cmd.GetCtrlSize() == Size(-1, -1))
|
|
bar.Add(enabled, *ctrl);
|
|
else
|
|
bar.Add(enabled, *ctrl, cmd.GetCtrlSize());
|
|
}
|
|
else
|
|
bar.Add(enabled, label, img, handler);
|
|
}
|
|
}
|
|
|
|
template<class T> void WithXMLMenu<T>::SetToolBar0(Bar &bar, int tbIdx, Array<XMLToolBarItem> const *items)
|
|
{
|
|
// get menu bar items
|
|
if(!items)
|
|
items = &toolBars[tbIdx].GetItems();
|
|
|
|
for(int i = 0; i < items->GetCount(); i++)
|
|
{
|
|
// get current item
|
|
XMLToolBarItem const &item = (*items)[i];
|
|
|
|
// submenu handling
|
|
if(item.IsSubMenu())
|
|
{
|
|
bar.Add(item.GetLabel(), item.GetIcon(), THISBACK2(SetToolBar0, tbIdx, &item.GetSubMenu().GetItems()));
|
|
continue;
|
|
}
|
|
|
|
// get command id and find command
|
|
// if not found, just skip it
|
|
String id = item.GetId();
|
|
if(!commands.Has(id))
|
|
continue;
|
|
|
|
// get command from id
|
|
XMLCommand const &cmd = commands.Get(id);
|
|
|
|
// get callback from command
|
|
bool isCustom = cmd.GetIsCustom();
|
|
Callback handler;
|
|
if(isCustom)
|
|
handler = THISBACK1(callUserHandler, cmd.GetCommandString());
|
|
else
|
|
handler = cmd.GetCallback();
|
|
|
|
// get remaining item data
|
|
String label = item.GetLabel();
|
|
if(label == "")
|
|
label = id;
|
|
Image const &img = item.GetIcon();
|
|
|
|
Ctrl *ctrl = cmd.GetCtrl();
|
|
bool enabled = cmd.GetIsEnabled();
|
|
|
|
// adds to menu
|
|
if(ctrl)
|
|
{
|
|
if(cmd.GetCtrlSize() == Size(-1, -1))
|
|
bar.Add(enabled, *ctrl);
|
|
else
|
|
bar.Add(enabled, *ctrl, cmd.GetCtrlSize());
|
|
}
|
|
else
|
|
bar.Add(enabled, label, img, handler);
|
|
}
|
|
}
|
|
|
|
// refresh menus and bars
|
|
template<class T> void WithXMLMenu<T>::RefreshBarsDeep(void)
|
|
{
|
|
// do a deep refresh, wiping all controls and recreating then
|
|
T::RemoveFrame(topFrame);
|
|
T::RemoveFrame(bottomFrame);
|
|
T::RemoveFrame(leftFrame);
|
|
T::RemoveFrame(rightFrame);
|
|
for(int iBar = 0; iBar < menuBarCtrls.GetCount(); iBar++)
|
|
T::RemoveFrame(menuBarCtrls[iBar]);
|
|
|
|
// refresh menus
|
|
menuBarCtrls.Clear();
|
|
for(int iBar = 0; iBar < menuBars.GetCount(); iBar++)
|
|
{
|
|
menuBarCtrls.Add(menuBars.GetKey(iBar), new MenuBar);
|
|
|
|
// workaround for main menu... without this one
|
|
// it appears as a popup menu
|
|
if(menuBars[iBar].GetName() == "Main")
|
|
T::AddFrame(menuBarCtrls[iBar]);
|
|
|
|
menuBarCtrls[iBar].Set(THISBACK1(SetMenuBar, iBar));
|
|
T::RemoveFrame(menuBarCtrls[iBar]);
|
|
}
|
|
|
|
// refresh toolbars
|
|
toolBarCtrls.Clear();
|
|
for(int iBar = 0; iBar < toolBars.GetCount(); iBar++)
|
|
{
|
|
XMLToolBar &toolBar = toolBars[iBar];
|
|
toolBarCtrls.Add(toolBars.GetKey(iBar), new XMLToolBarCtrl(this));
|
|
XMLToolBarCtrl &toolBarCtrl = toolBarCtrls.Top();
|
|
toolBarCtrls[iBar].Set(THISBACK1(SetToolBar, iBar));
|
|
Reposition(&toolBarCtrl, toolBar.GetState(), toolBar.GetPosition());
|
|
toolBarCtrls[iBar].SetPrevState(toolBar.GetPrevState());
|
|
}
|
|
|
|
// refresh frames
|
|
RefreshFrames();
|
|
}
|
|
|
|
template<class T> void WithXMLMenu<T>::RefreshBars(void)
|
|
{
|
|
// refactored logic for toolbar updates -- avoids unneeded
|
|
// screen redrawing and frame adding/removing
|
|
|
|
// we shall first find if controls were added or removed
|
|
// in that case we proceed the old way, recalculating all frames
|
|
Vector<String> const &ctrlNames = toolBarCtrls.GetKeys();
|
|
bool deepUpdate = false;
|
|
for(int iName = 0; iName < ctrlNames.GetCount(); iName++)
|
|
{
|
|
if(toolBars.Find(ctrlNames[iName]) < 0)
|
|
{
|
|
deepUpdate = true;
|
|
break;
|
|
}
|
|
}
|
|
if(!deepUpdate)
|
|
{
|
|
Vector<String> const &barNames = toolBars.GetKeys();
|
|
for(int iName = 0; iName < barNames.GetCount(); iName++)
|
|
{
|
|
if(toolBarCtrls.Find(barNames[iName]) < 0)
|
|
{
|
|
deepUpdate = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(!deepUpdate)
|
|
{
|
|
Vector<String> const &menuCtrlNames = menuBars.GetKeys();
|
|
for(int iName = 0; iName < menuCtrlNames.GetCount(); iName++)
|
|
{
|
|
if(menuBars.Find(menuCtrlNames[iName]) < 0)
|
|
{
|
|
deepUpdate = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if(!deepUpdate)
|
|
{
|
|
Vector<String> const &menuNames = menuBars.GetKeys();
|
|
for(int iName = 0; iName < menuNames.GetCount(); iName++)
|
|
{
|
|
if(menuBarCtrls.Find(menuNames[iName]) < 0)
|
|
{
|
|
deepUpdate = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(deepUpdate)
|
|
RefreshBarsDeep();
|
|
else
|
|
{
|
|
// do a smart refresh, just update controls inside menus and toolbars
|
|
for(int iBar = 0; iBar < menuBars.GetCount(); iBar++)
|
|
menuBarCtrls[iBar].Set(THISBACK1(SetMenuBar, iBar));
|
|
for(int iBar = 0; iBar < toolBars.GetCount(); iBar++)
|
|
toolBarCtrls[iBar].Set(THISBACK1(SetToolBar, iBar));
|
|
}
|
|
}
|
|
|
|
// sync bars from ctrls
|
|
template<class T> void WithXMLMenu<T>::SyncBars(void)
|
|
{
|
|
for(int i = 0; i < toolBars.GetCount(); i++)
|
|
{
|
|
XMLToolBarCtrl const &c = toolBarCtrls[i];
|
|
Point p = c.GetPosition();
|
|
XMLToolBarState state = c.GetState();
|
|
XMLToolBarState prevState = c.GetPrevState();
|
|
toolBars[i].SetPosition(p).SetState(state).SetPrevState(prevState);
|
|
}
|
|
}
|
|
|
|
// sets builtin commands
|
|
template<class T> void WithXMLMenu<T>::SetCommands(Callback1<XMLCommands &> cmds)
|
|
{
|
|
// setup commands
|
|
cmds(commands);
|
|
|
|
// refresh bars and frames
|
|
RefreshBars();
|
|
}
|
|
|
|
// sets menu entries
|
|
template<class T> void WithXMLMenu<T>::SetMenuBars(Callback1<XMLToolBars &> tb)
|
|
{
|
|
tb(menuBars);
|
|
|
|
// refresh toolbars
|
|
RefreshBars();
|
|
}
|
|
|
|
// sets toolbars entries
|
|
template<class T> void WithXMLMenu<T>::SetToolBars(Callback1<XMLToolBars &> tb)
|
|
{
|
|
tb(toolBars);
|
|
|
|
// refresh toolbars
|
|
RefreshBars();
|
|
}
|
|
|
|
// gets a context menu by name -- NULL if none
|
|
template<class T> MenuBar *WithXMLMenu<T>::GetContextMenu(String const &name)
|
|
{
|
|
return menuBarCtrls.FindPtr(name);
|
|
}
|
|
|
|
// xml support
|
|
template<class T> void WithXMLMenu<T>::Xmlize(XmlIO xml)
|
|
{
|
|
if(xml.IsStoring())
|
|
SyncBars();
|
|
xml
|
|
("commands" , commands)
|
|
("menubars" , menuBars)
|
|
("toolbars" , toolBars)
|
|
;
|
|
// here too, we use postcallback because we must
|
|
// wait that top window is opened before refreshing toolbars
|
|
// don't know if it's the right way, but.....
|
|
if(xml.IsLoading())
|
|
T::PostCallback(THISBACK(RefreshBarsDeep));
|
|
}
|
|
|
|
// toggles bar visibility
|
|
template<class T> void WithXMLMenu<T>::toggleBarCb(int iBar)
|
|
{
|
|
XMLToolBarCtrl &tb = toolBarCtrls[iBar];
|
|
if(tb.GetState() == TOOLBAR_CLOSED)
|
|
{
|
|
switch(tb.prevState)
|
|
{
|
|
case TOOLBAR_LEFT :
|
|
case TOOLBAR_RIGHT :
|
|
case TOOLBAR_TOP :
|
|
case TOOLBAR_BOTTOM :
|
|
case TOOLBAR_FLOATING :
|
|
Reposition(&tb, tb.prevState, tb.toolBarPos);
|
|
break;
|
|
default:
|
|
Reposition(&tb, TOOLBAR_FLOATING, tb.toolBarPos);
|
|
}
|
|
}
|
|
else
|
|
tb.CloseBar();
|
|
T::RefreshLayout();
|
|
}
|
|
|
|
// right click context menu
|
|
template<class T> void WithXMLMenu<T>::XMLContextMenu(Bar& bar)
|
|
{
|
|
// add customize command
|
|
bar.Add(t_("Customize"), THISBACK(runEditorCb));
|
|
|
|
// allow open/close bars
|
|
if(!toolBars.GetCount())
|
|
return;
|
|
bar.Separator();
|
|
for(int iBar = 0; iBar < toolBars.GetCount(); iBar++)
|
|
{
|
|
XMLToolBarCtrl &toolBarCtrl = toolBarCtrls[iBar];
|
|
String const &barName = toolBars.GetKey(iBar);
|
|
Bar::Item &item = bar.Add(barName, THISBACK1(toggleBarCb, iBar));
|
|
item.Check(toolBarCtrl.GetState() != TOOLBAR_CLOSED);
|
|
}
|
|
}
|
|
|
|
END_UPP_NAMESPACE
|
|
|
|
#endif
|