X-Git-Url: http://git.salome-platform.org/gitweb/?a=blobdiff_plain;f=src%2FQtx%2FQtxActionMenuMgr.cxx;h=54704d0c73584779f250f4d71e0822d43913b47b;hb=00be77fd72d1e9378e32b88a468ec16bebb9b806;hp=4cacc54cf83a1cd75f6b91f9bc1a632299200b81;hpb=2e750f9ded92337bc3c44e9d7388180974cc4a43;p=modules%2Fgui.git diff --git a/src/Qtx/QtxActionMenuMgr.cxx b/src/Qtx/QtxActionMenuMgr.cxx index 4cacc54cf..54704d0c7 100644 --- a/src/Qtx/QtxActionMenuMgr.cxx +++ b/src/Qtx/QtxActionMenuMgr.cxx @@ -1,150 +1,178 @@ -// File: QtxActionMenuMgr.cxx -// Author: Alexander SOLOVYEV, Sergey TELKOV +// Copyright (C) 2007-2016 CEA/DEN, EDF R&D, OPEN CASCADE +// +// Copyright (C) 2003-2007 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN, +// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// +// File: QtxActionMenuMgr.cxx +// Author: Alexander SOLOVYOV, Sergey TELKOV +// #include "QtxActionMenuMgr.h" #include "QtxAction.h" -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include /*! - Class: QtxActionMenuMgr::MenuAction - Level: Internal + \class QtxActionMenuMgr::MenuNode + \brief Represents a menu item inside main menu structure. + \internal */ -class QtxActionMenuMgr::MenuAction : public QtxAction +class QtxActionMenuMgr::MenuNode { public: - MenuAction( const QString&, const QString&, QObject* ); - virtual ~MenuAction(); - - virtual bool addTo( QWidget* ); - - virtual bool removeFrom( QWidget* ); - - QPopupMenu* popup() const; - -private: - int myId; - QPopupMenu* myPopup; + MenuNode(); + MenuNode( MenuNode*, const int, const int, const int ); + ~MenuNode(); + + MenuNode* parent; //!< parent menu node + int id; //!< menu nodeID + int idx; //!< menu node index + int group; //!< menu group ID + bool visible; //!< visibility status + int emptyEnabled; //!< enable empty menu flag + NodeList children; //!< children menu nodes list }; -QtxActionMenuMgr::MenuAction::MenuAction( const QString& text, const QString& menuText, QObject* parent ) -: QtxAction( text, menuText, 0, parent ), -myId( -1 ), -myPopup( 0 ) +/*! + \brief Default constructor. + \internal +*/ +QtxActionMenuMgr::MenuNode::MenuNode() + : parent( 0 ), id( -1 ), idx( -1 ), group( -1 ), visible( true ), emptyEnabled( 0 ) { - myPopup = new QPopupMenu(); } -QtxActionMenuMgr::MenuAction::~MenuAction() +/*! + \brief Constructor. + \internal + \param p parent menu node + \param _id menu node ID + \param _idx menu node index + \param _group menu node group ID +*/ +QtxActionMenuMgr::MenuNode::MenuNode( MenuNode* p, + const int _id, + const int _idx, + const int _group ) +: parent( p ), id( _id ), idx( _idx ), group( _group ), visible( true ), emptyEnabled( 0 ) { - delete myPopup; + if ( p ) + p->children.append( this ); } -bool QtxActionMenuMgr::MenuAction::addTo( QWidget* w ) +/*! + \brief Destructor. + \internal +*/ +QtxActionMenuMgr::MenuNode::~MenuNode() { - if ( myId != -1 || !w ) - return false; - - if ( !w->inherits( "QPopupMenu" ) && !w->inherits( "QMenuBar" ) ) - return false; + for ( NodeList::iterator it = children.begin(); it != children.end(); ++it ) + delete *it; +} - if ( w->inherits( "QPopupMenu" ) && QAction::addTo( w ) ) - { - QPopupMenu* pm = (QPopupMenu*)w; - myId = pm->idAt( pm->count() - 1 ); - setPopup( pm, myId, myPopup ); - } - else if ( w->inherits( "QMenuBar" ) ) - { - QMenuBar* mb = (QMenuBar*)w; - myId = iconSet().isNull() ? mb->insertItem( menuText(), myPopup ) : - mb->insertItem( iconSet(), menuText(), myPopup ); - mb->setItemEnabled( myId, isEnabled() ); - } - else - return false; +/*! + \class QtxActionMenuMgr + \brief Main menu actions manager. - return true; -} + Menu manager allows using of set of action for automatic generating of + application main menu and dynamic update of its contents. -bool QtxActionMenuMgr::MenuAction::removeFrom( QWidget* w ) -{ - if ( w->inherits( "QPopupMenu" ) && QAction::removeFrom( w ) ) - myId = -1; - else if ( w->inherits( "QMenuBar" ) ) - { - QMenuBar* mb = (QMenuBar*)w; - mb->removeItem( myId ); - myId = -1; - } + Use insert(), append() and remove() methods to create main menu. + Methods show(), hide() allow displaying/erasing of specified menu items. - return myId == -1; -} + Actions can be grouped with help of group identificator. Inside the popup + or main menu bar menu items are ordered by the group identifier (ascending). -QPopupMenu* QtxActionMenuMgr::MenuAction::popup() const -{ - return myPopup; -} + Menu manager automatically optimizes the menu by removing extra separators, + hiding empty popup menus etc. +*/ /*! - Class: QtxActionMenuMgr - Level: Public + \brief Constructor. + \param p parent main window */ - - QtxActionMenuMgr::QtxActionMenuMgr( QMainWindow* p ) -: QtxActionMgr( p ), -myMenu( p ? p->menuBar() : 0 ) +: QtxActionMgr( p ), + myRoot( new MenuNode() ), + myMenu( p ? p->menuBar() : 0 ) { - myRoot.id = -1; - myRoot.group = -1; - - if ( myMenu ) + if ( myMenu ) { connect( myMenu, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) ); + } } +/*! + \brief Constructor. + \param mw menu widget + \param p parent object +*/ QtxActionMenuMgr::QtxActionMenuMgr( QWidget* mw, QObject* p ) -: QtxActionMgr( p ), -myMenu( mw ) +: QtxActionMgr( p ), + myRoot( new MenuNode() ), + myMenu( mw ) { - myRoot.id = -1; - myRoot.group = -1; - - if ( myMenu ) + if ( myMenu ) { connect( myMenu, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) ); + } } +/*! + \brief Destructor. +*/ QtxActionMenuMgr::~QtxActionMenuMgr() { - for ( NodeListIterator it( myRoot.children ); it.current(); ++it ) + for ( MenuMap::Iterator itr = myMenus.begin(); itr != myMenus.end(); ++itr ) { - QAction* a = itemAction( it.current()->id ); - if ( !a ) - a = menuAction( it.current()->id ); - - if ( a ) - a->removeFrom( myMenu ); + QPointer a = itr.value(); + delete a->menu(); + delete a; } - for ( MenuMap::Iterator itr = myMenus.begin(); itr != myMenus.end(); ++itr ) - delete itr.data(); + delete myRoot; } +/*! + \brief Check if an action with \a actId identifier is visible to + the parent action with \a place identifier. + \param actId action ID + \param place some parent action ID + \return \c true if an action is visible to the parent + \sa setVisible() +*/ bool QtxActionMenuMgr::isVisible( const int actId, const int place ) const { MenuNode* node = find( actId, place ); return node && node->visible; } +/*! + \brief Set action's visibility flag. + \param actId action ID + \param place some parent action ID + \param v new visibility state + \sa isVisible() +*/ void QtxActionMenuMgr::setVisible( const int actId, const int place, const bool v ) { MenuNode* node = find( actId, place ); @@ -152,16 +180,59 @@ void QtxActionMenuMgr::setVisible( const int actId, const int place, const bool node->visible = v; } +/*! + \brief Insert action to the menu. + + Insert an action to the named menu. The \a menus parameter represents + the menu name: it can be a sequence of strings, separated by '|' symbol. + For example, "File|Edit" means \c File->Edit submenu. + If submenu doesn't exist, it will be created. + + \param id action ID + \param menus menu name + \param group group ID + \param idx menu index inside the menu group + \return action ID +*/ int QtxActionMenuMgr::insert( const int id, const QString& menus, const int group, const int idx ) { - return insert( id, QStringList::split( "|", menus ), group, idx ); + return insert( id, menus.split( "|", QString::SkipEmptyParts ), group, idx ); } +/*! + \brief Insert action to the menu. + + Insert an action to the named menu. The \a menus parameter represents + the menu name: it can be a sequence of strings, separated by '|' symbol. + For example, "File|Edit" means \c File->Edit submenu. + If submenu doesn't exist, it will be created. + + \param a action + \param menus menu name + \param group group ID + \param idx menu index inside the menu group + \return action ID +*/ int QtxActionMenuMgr::insert( QAction* a, const QString& menus, const int group, const int idx ) { - return insert( a, QStringList::split( "|", menus ), group, idx ); + return insert( a, menus.split( "|", QString::SkipEmptyParts ), group, idx ); } +/*! + \brief Insert action to the menu. + + Insert an action to the named menu. The \a menus parameter represents + the menu names list. + For example, string list consisting from two items "File" and "Edit" + means \c File->Edit submenu. + If submenu doesn't exist, it will be created. + + \param id action ID + \param menus menu names list + \param group group ID + \param idx menu index inside the menu group + \return action ID +*/ int QtxActionMenuMgr::insert( const int id, const QStringList& menus, const int group, const int idx ) { int pId = createMenu( menus, -1 ); @@ -171,6 +242,21 @@ int QtxActionMenuMgr::insert( const int id, const QStringList& menus, const int return insert( id, pId, group, idx ); } +/*! + \brief Insert action to the menu. + + Insert an action to the named menu. The \a menus parameter represents + the menu names list. + For example, string list consisting from two items "File" and "Edit" + means \c File->Edit submenu. + If submenu doesn't exist, it will be created. + + \param a action + \param menus menu names list + \param group group ID + \param idx menu index inside the menu group + \return action ID +*/ int QtxActionMenuMgr::insert( QAction* a, const QStringList& menus, const int group, const int idx ) { int pId = createMenu( menus, -1 ); @@ -180,143 +266,280 @@ int QtxActionMenuMgr::insert( QAction* a, const QStringList& menus, const int gr return insert( a, pId, group, idx ); } +/*! + \brief Insert action to the menu. + \param id action ID + \param pId parent menu action ID + \param group group ID + \param idx menu index inside the menu group + \return action ID +*/ int QtxActionMenuMgr::insert( const int id, const int pId, const int group, const int idx ) { if ( id == -1 ) return -1; - MenuNode* pNode = pId == -1 ? &myRoot : find( pId ); + MenuNode* pNode = pId == -1 ? myRoot : find( pId ); if ( !pNode ) return -1; - MenuNode* node = new MenuNode( pNode ); - node->id = id; - node->group = group; + MenuNode* node = new MenuNode( pNode, id, idx, group ); - if ( idx < 0 || idx >= (int)pNode->children.count() ) - pNode->children.append( node ); - else - pNode->children.insert( idx, node ); - - updateMenu( pNode, false ); + triggerUpdate( pNode->id, false ); return node->id; } +/*! + \brief Insert action to the menu. + \param a action + \param pId parent menu action ID + \param group group ID + \param idx menu index inside the menu group + \return action ID +*/ int QtxActionMenuMgr::insert( QAction* a, const int pId, const int group, const int idx ) { return insert( registerAction( a ), pId, group, idx ); } -int QtxActionMenuMgr::insert( const QString& title, const int pId, const int group, const int idx ) +/*! + \brief Create and insert menu item action to the menu. + \param title menu text + \param pId parent menu action ID + \param group group ID + \param id action ID + \param idx menu index inside the menu group + \return action ID +*/ +int QtxActionMenuMgr::insert( const QString& title, const int pId, const int group, + const int id, const int idx, QMenu* _menu) { - MenuNode* pNode = pId == -1 ? &myRoot : find( pId ); + MenuNode* pNode = pId == -1 ? myRoot : find( pId ); if ( !pNode ) return -1; - int id = -1; - for ( NodeListIterator it( pNode->children ); it.current() && id == -1; ++it ) + MenuNode* eNode = id == -1 ? 0 : find( id ); + + int fid = -1; + for ( NodeList::iterator it = pNode->children.begin(); it != pNode->children.end() && fid == -1; ++it ) { - if ( myMenus.contains( it.current()->id ) && - clearTitle( myMenus[it.current()->id]->menuText() ) == clearTitle( title ) ) - id = it.current()->id; + if ( myMenus.contains( (*it)->id ) && + clearTitle( myMenus[(*it)->id]->text() ) == clearTitle( title ) ) + fid = (*it)->id; } - if ( id != -1 ) - return id; + if ( fid != -1 ) + return fid; - MenuAction* ma = new MenuAction( clearTitle( title ), title, this ); + int gid = (id == -1 || eNode ) ? generateId() : id; - MenuNode* node = new MenuNode( pNode ); - node->group = group; - node->id = myMenus.insert( generateId(), ma ).key(); - - if ( idx < 0 || idx >= (int)pNode->children.count() ) - pNode->children.append( node ); + QMenu* menu; + if (_menu) + menu = _menu; else - pNode->children.insert( idx, node ); + menu = new QMenu( 0 ); + QAction* ma = menu->menuAction(); + ma->setText( title ); + + connect( ma->menu(), SIGNAL( aboutToShow() ), this, SLOT( onAboutToShow() ) ); + connect( ma->menu(), SIGNAL( aboutToHide() ), this, SLOT( onAboutToHide() ) ); + + MenuNode* node = new MenuNode( pNode, myMenus.insert( gid, ma ).key(), idx, group ); - updateMenu( pNode, false ); + triggerUpdate( pNode->id, false ); return node->id; } -int QtxActionMenuMgr::insert( const QString& title, const QString& menus, const int group, const int idx ) +/*! + \brief Create and insert menu item action to the menu. + + Insert an action to the named menu. The \a menus parameter represents + the menu name: it can be a sequence of strings, separated by '|' symbol. + For example, "File|Edit" means \c File->Edit submenu. + If submenu doesn't exist, it will be created. + + \param title menu text + \param menus menu name + \param group group ID + \param id action ID + \param idx menu index inside the menu group + \return action ID +*/ +int QtxActionMenuMgr::insert( const QString& title, const QString& menus, const int group, const int id, const int idx ) { - return insert( title, QStringList::split( "|", menus ), group, idx ); + return insert( title, menus.split( "|", QString::SkipEmptyParts ), group, id, idx ); } -int QtxActionMenuMgr::insert( const QString& title, const QStringList& menus, const int group, const int idx ) +/*! + \brief Create and insert menu item action to the menu. + + Insert an action to the named menu. The \a menus parameter represents + the menu names list. + For example, string list consisting from two items "File" and "Edit" + means \c File->Edit submenu. + If submenu doesn't exist, it will be created. + + \param title menu text + \param menus menu names list + \param group group ID + \param id action ID + \param idx menu index inside the menu group + \return action ID +*/ +int QtxActionMenuMgr::insert( const QString& title, const QStringList& menus, const int group, const int id, const int idx ) { int pId = createMenu( menus, -1 ); - return insert( title, pId, group, idx ); + return insert( title, pId, group, id, idx ); } -int QtxActionMenuMgr::append( const QString& title, const int pId, const int group ) +/*! + \brief Create and add menu item action to the end of menu. + \param title menu text + \param pId parent menu action ID + \param group group ID + \param id action ID + \return action ID +*/ +int QtxActionMenuMgr::append( const QString& title, const int pId, const int group, const int id ) { - return insert( title, pId, group ); + return insert( title, pId, group, id ); } +/*! + \brief Create and add menu item action to the end of menu. + \param id action ID + \param pId parent menu action ID + \param group group ID + \return action ID +*/ int QtxActionMenuMgr::append( const int id, const int pId, const int group ) { return insert( id, pId, group ); } +/*! + \brief Create and add menu item action to the end of menu. + \param a action + \param pId parent menu action ID + \param group group ID + \return action ID +*/ int QtxActionMenuMgr::append( QAction* a, const int pId, const int group ) { return insert( a, pId, group ); } -int QtxActionMenuMgr::prepend( const QString& title, const int pId, const int group ) +/*! + \brief Create and add menu item action to the beginning of menu. + \param title menu text + \param pId parent menu action ID + \param group group ID + \param id action ID + \return action ID +*/ +int QtxActionMenuMgr::prepend( const QString& title, const int pId, const int group, const int id ) { - return insert( title, pId, group, 0 ); + return insert( title, pId, group, id, 0 ); } +/*! + \brief Create and add menu item action to the beginning of menu. + \param id action ID + \param pId parent menu action ID + \param group group ID + \return action ID +*/ int QtxActionMenuMgr::prepend( const int id, const int pId, const int group ) { return insert( id, pId, group, 0 ); } +/*! + \brief Create and add menu item action to the beginning of menu. + \param a action + \param pId parent menu action ID + \param group group ID + \return action ID +*/ int QtxActionMenuMgr::prepend( QAction* a, const int pId, const int group ) { return insert( a, pId, group, 0 ); } +/*! + \brief Remove menu item with given \a id. + \param id menu action ID +*/ void QtxActionMenuMgr::remove( const int id ) { removeMenu( id, 0 ); update(); } +/*! + \brief Remove menu item with given \a id. + \param id menu action ID + \param pId parent menu action ID + \param group group ID +*/ void QtxActionMenuMgr::remove( const int id, const int pId, const int group ) { - MenuNode* pNode = find( pId ); + MenuNode* pNode = pId == -1 ? myRoot : find( pId ); if ( !pNode ) return; NodeList delNodes; - for ( NodeListIterator it( pNode->children ); it.current(); ++it ) + for ( NodeList::iterator it = pNode->children.begin(); it != pNode->children.end(); ++it ) { - if ( it.current()->id == id && ( it.current()->group == group || group == -1 ) ) - delNodes.append( it.current() ); + if ( (*it)->id == id && ( (*it)->group == group || group == -1 ) ) + delNodes.append( *it ); } - for ( NodeListIterator itr( delNodes ); itr.current(); ++itr ) - pNode->children.remove( itr.current() ); + QWidget* mW = menuWidget( pNode ); + for ( NodeList::iterator itr = delNodes.begin(); itr != delNodes.end(); ++itr ) + { + int id = (*itr)->id; + if( mW && menuAction( id ) ) + { + mW->removeAction( menuAction( id ) ); + myMenus.remove( id ); + } + else if( mW && itemAction( id ) ) + mW->removeAction( itemAction( id ) ); + pNode->children.removeAll( *itr ); + } - updateMenu( pNode, false ); + triggerUpdate( pNode->id, false ); } +/*! + \brief Show menu item with given \a id. + \param id menu action ID + \sa hide() +*/ void QtxActionMenuMgr::show( const int id ) { setShown( id, true ); } +/*! + \brief Hide menu item with given \a id. + \param id menu action ID + \sa show() +*/ void QtxActionMenuMgr::hide( const int id ) { setShown( id, false ); } +/*! + \brief Get visibility status for menu item with given \a id. + \param id menu action ID + \return \c true if an item is shown + \sa setShown() +*/ bool QtxActionMenuMgr::isShown( const int id ) const { bool res = false; @@ -326,32 +549,103 @@ bool QtxActionMenuMgr::isShown( const int id ) const return res; } +/*! + \brief Set visibility status for menu item with given \a id. + \param id menu action ID + \param on new visibility status + \sa isShown() +*/ void QtxActionMenuMgr::setShown( const int id, const bool on ) { NodeList aNodes; find( id, aNodes ); - QMap updMap; - for ( NodeListIterator it( aNodes ); it.current(); ++it ) + for ( NodeList::iterator it = aNodes.begin(); it != aNodes.end(); ++it ) { - if ( it.current()->visible != on ) + if ( (*it)->visible != on ) { - it.current()->visible = on; - updMap.insert( it.current()->parent, 0 ); + (*it)->visible = on; + triggerUpdate( (*it)->parent ? (*it)->parent->id : myRoot->id, false ); } } +} + +/*! + \brief Change menu title for the action with given \a id. + \param id menu action ID + \param title new menu title +*/ +void QtxActionMenuMgr::change( const int id, const QString& title ) +{ + QAction* a = menuAction( id ); + if ( a ) + a->setText( title ); +} + +/*! + \brief Called when the submenu is about to show. + + Emits the signal menuAboutToShow(QMenu*). +*/ +void QtxActionMenuMgr::onAboutToShow() +{ + QMenu* m = ::qobject_cast( sender() ); + if ( m ) + emit menuAboutToShow( m ); +} - for ( QMap::ConstIterator itr = updMap.begin(); itr != updMap.end(); ++itr ) - updateMenu( itr.key(), false ); +/*! + \brief Called when the submenu is about to hide. + + Emits the signal menuAboutToHide(QMenu*). +*/ +void QtxActionMenuMgr::onAboutToHide() +{ + QMenu* m = ::qobject_cast( sender() ); + if ( m ) + emit menuAboutToHide( m ); } +/*! + \brief Called when the corresponding menu object is destroyed. + + Clears internal pointer to menu to disable crashes. + + \param obj (menu) object being destroyed +*/ void QtxActionMenuMgr::onDestroyed( QObject* obj ) { if ( myMenu == obj ) myMenu = 0; } -void QtxActionMenuMgr::setWidget( QWidget* mw ) + +/*! + \fn void QtxActionMenuMgr::menuAboutToShow( QMenu* m ) + \brief Emitted when the menu is about to be shown. + \param m menu being shown +*/ + +/*! + \fn void QtxActionMenuMgr::menuAboutToHide( QMenu* m ) + \brief Emitted when the menu is about to be hidden. + \param m menu being hidden +*/ + +/*! + \brief Get the menu widget. + \return menu widget (QMenuBar) +*/ +QWidget* QtxActionMenuMgr::menuWidget() const +{ + return myMenu; +} + +/*! + \brief Assign new menu widget to the menu manager. + \param mw new menu widget +*/ +void QtxActionMenuMgr::setMenuWidget( QWidget* mw ) { if ( myMenu == mw ) return; @@ -363,60 +657,178 @@ void QtxActionMenuMgr::setWidget( QWidget* mw ) if ( myMenu ) connect( myMenu, SIGNAL( destroyed( QObject* ) ), this, SLOT( onDestroyed( QObject* ) ) ); + + triggerUpdate( -1, true ); } -QtxActionMenuMgr::MenuNode* QtxActionMenuMgr::find( const int actId, const int pId ) const +/*! + \brief Search menu node. + \param id menu action ID + \param pId parent menu item ID + \param rec if \c true perform recursive search + \return menu node or 0 if it is not found +*/ +QtxActionMenuMgr::MenuNode* QtxActionMenuMgr::find( const int id, const int pId, const bool rec ) const { - return find( actId, find( pId ) ); + return find( id, find( pId ), rec ); } -QtxActionMenuMgr::MenuNode* QtxActionMenuMgr::find( const int id, MenuNode* startNode ) const +/*! + \brief Search menu node. + \param id menu action ID + \param startNode start menu node (if 0, search from root node) + \param rec if \c true perform recursive search + \return menu node or 0 if it is not found +*/ +QtxActionMenuMgr::MenuNode* QtxActionMenuMgr::find( const int id, MenuNode* startNode, const bool rec ) const { MenuNode* node = 0; - MenuNode* start = startNode ? startNode : (MenuNode*)&myRoot; - for ( NodeListIterator it( start->children ); it.current() && !node; ++it ) + MenuNode* start = startNode ? startNode : myRoot; + for ( NodeList::iterator it = start->children.begin(); it != start->children.end() && !node; ++it ) { - if ( it.current()->id == id ) - node = it.current(); - else - node = find( id, it.current() ); + if ( (*it)->id == id ) + node = *it; + else if ( rec ) + node = find( id, *it, rec ); } return node; } +/*! + \brief Search recursively all menu nodes with given \a id. + \param id menu action ID + \param NodeList resulting list of menu nodes + \param startNode start menu node (if 0, search from root node) + \return \c true if at least one node is found +*/ bool QtxActionMenuMgr::find( const int id, NodeList& lst, MenuNode* startNode ) const { - MenuNode* start = startNode ? startNode : (MenuNode*)&myRoot; - for ( NodeListIterator it( start->children ); it.current(); ++it ) + MenuNode* start = startNode ? startNode : myRoot; + for ( NodeList::iterator it = start->children.begin(); it != start->children.end(); ++it ) + { + MenuNode* node = *it; + if ( node->id == id ) + lst.prepend( node ); + + find( id, lst, node ); + } + return !lst.isEmpty(); +} + +/*! + \brief Search menu node. + \param title menu item title + \param pId parent menu item ID + \param rec if \c true perform recursive search + \return menu node or 0 if it is not found +*/ +QtxActionMenuMgr::MenuNode* QtxActionMenuMgr::find( const QString& title, const int pId, const bool rec ) const +{ + return find( title, find( pId ), rec ); +} + +/*! + \brief Search recursively all menu nodes with given \a title. + \param title menu item title + \param NodeList resulting list of menu nodes + \param startNode start menu node (if 0, search from root node) + \return \c true if at least one node is found +*/ +bool QtxActionMenuMgr::find( const QString& title, NodeList& lst, MenuNode* startNode ) const +{ + MenuNode* start = startNode ? startNode : myRoot; + for ( NodeList::iterator it = start->children.begin(); it != start->children.end(); ++it ) { - if ( it.current()->id == id ) - lst.prepend( it.current() ); + QAction* a = itemAction( (*it)->id ); + if ( !a ) + a = menuAction( (*it)->id ); + if ( a && clearTitle( a->text() ) == clearTitle( title ) ) + lst.prepend( *it ); - find( id, lst, it.current() ); + find( title, lst, *it ); } return !lst.isEmpty(); } +/*! + \brief Search menu node. + \param title menu item title + \param startNode start menu node (if 0, search from root node) + \param rec if \c true perform recursive search + \return menu node or 0 if it is not found +*/ +QtxActionMenuMgr::MenuNode* QtxActionMenuMgr::find( const QString& title, MenuNode* startNode, const bool rec ) const +{ + MenuNode* node = 0; + MenuNode* start = startNode ? startNode : myRoot; + for ( NodeList::iterator it = start->children.begin(); it != start->children.end() && !node; ++it ) + { + QAction* a = itemAction( (*it)->id ); + if ( !a ) + a = menuAction( (*it)->id ); + if ( a && clearTitle( a->text() ) == clearTitle( title ) ) + node = *it; + if ( !node && rec ) + node = find( title, *it, rec ); + } + return node; +} + +/*! + \brief Find menu item by given ID (one-level only). + \param id menu action ID + \param pid parent meun item ID + \return id (>0) on success or -1 if menu item is not found +*/ +int QtxActionMenuMgr::findId( const int id, const int pid ) const +{ + MenuNode* start = pid != -1 ? find( pid ) : myRoot; + if ( start ) + { + for ( NodeList::iterator it = start->children.begin(); it != start->children.end(); ++it ) + { + if ( (*it)->id == id ) + return id; + } + } + return -1; +} + +/*! + \brief Removes menu node (with all its children). + \param id menu action ID + \param startNode parent menu node which search starts from (if 0, search starts from root) +*/ void QtxActionMenuMgr::removeMenu( const int id, MenuNode* startNode ) { - MenuNode* start = startNode ? startNode : &myRoot; - for ( NodeListIterator it( start->children ); it.current(); ++it ) + MenuNode* start = startNode ? startNode : myRoot; + for ( NodeList::iterator it = start->children.begin(); it != start->children.end(); ++it ) { - if ( it.current()->id == id ) - start->children.remove( it.current() ); + if ( (*it)->id == id ) + start->children.removeAll( *it ); else - removeMenu( id, it.current() ); + removeMenu( id, *it ); } } +/*! + \brief Get action by \a id. + \param id action ID + \return action or 0 if \a id is invalid +*/ QAction* QtxActionMenuMgr::itemAction( const int id ) const { return action( id ); } -QtxActionMenuMgr::MenuAction* QtxActionMenuMgr::menuAction( const int id ) const +/*! + \brief Get submenu action by \a id. + \param id submenu ID + \return submenu action or 0 if action is not found +*/ +QAction* QtxActionMenuMgr::menuAction( const int id ) const { - MenuAction* a = 0; + QAction* a = 0; if ( myMenus.contains( id ) ) a = myMenus[id]; @@ -424,120 +836,227 @@ QtxActionMenuMgr::MenuAction* QtxActionMenuMgr::menuAction( const int id ) const return a; } +/*! + \brief Get submenu action by \a id. + \param id submenu ID + \return submenu action or 0 if it is not found +*/ +int QtxActionMenuMgr::menuActionId( QAction* a ) const +{ + int id = -1; + for ( MenuMap::ConstIterator itr = myMenus.begin(); itr != myMenus.end() && id == -1; ++itr ) + { + if ( itr.value() == a ) + id = itr.key(); + } + return id; +} + +/*! + \brief Update menu. + + Does nothing if update is disabled. + + \param startNode start menu item to be updated + \param rec if \c true, perform recursive update + \param updParent if \c true update also parent item (without recursion) + + \sa isUpdatesEnabled() and setUpdatesEnabled() +*/ void QtxActionMenuMgr::updateMenu( MenuNode* startNode, const bool rec, const bool updParent ) { if ( !isUpdatesEnabled() ) return; - MenuNode* node = startNode ? startNode : &myRoot; + MenuNode* node = startNode ? startNode : myRoot; QWidget* mw = menuWidget( node ); if ( !mw ) return; - bool filled = checkWidget( mw ); - - for ( NodeListIterator it1( node->children ); it1.current(); ++it1 ) + // first remove all own actions and collect foreign ones + QMap< QAction*, QList > foreign; + QAction* a; + QAction* preva = 0; + QListIterator ait( mw->actions() ); ait.toBack(); + while ( ait.hasPrevious() ) { - QAction* a = itemAction( it1.current()->id ); - if ( !a ) - a = menuAction( it1.current()->id ); - - if ( a ) - a->removeFrom( mw ); + a = ait.previous(); + if ( ownAction( a, node ) ) + { + preva = a; + mw->removeAction( a ); // remove own actions + } + else + { + foreign[preva].prepend(a); // do not yet remove foreign actions + } } - - if ( node != &myRoot ) + // now only foreign actions should stay in the menu, thus remove them also + QMap< QAction*, QList >::Iterator formapit; + for( formapit = foreign.begin(); formapit != foreign.end(); ++formapit ) { - if ( mw->inherits( "QMenuBar" ) ) - ((QMenuBar*)mw)->clear(); - else if ( mw->inherits( "QPopupMenu" ) ) - ((QPopupMenu*)mw)->clear(); + QMutableListIterator foralit( formapit.value() ); + while ( foralit.hasNext() ) + { + a = foralit.next(); + if ( !mw->actions().contains( a ) ) + foralit.remove(); + } } + QList alist = mw->actions(); + foreach( a, alist ) mw->removeAction( a ); + // collect all registered menus by group id QMap idMap; - for ( NodeListIterator it2( node->children ); it2.current(); ++it2 ) + for ( NodeList::iterator it2 = node->children.begin(); it2 != node->children.end(); ++it2 ) { - MenuNode* par = it2.current()->parent; - if ( isVisible( it2.current()->id, par ? par->id : -1 ) ) - { - NodeList& lst = idMap[it2.current()->group]; - lst.append( it2.current() ); - } + NodeList& lst = idMap[(*it2)->group]; + int idx = (*it2)->idx; + if ( idx < 0 || idx >= (int)lst.count() ) + lst.append( *it2 ); + else + lst.insert( idx, *it2 ); } QIntList groups = idMap.keys(); - qHeapSort( groups ); + qSort( groups ); - groups.remove( -1 ); + groups.removeAll( -1 ); groups.append( -1 ); + // rebuild menu: 1. add all registered actions for ( QIntList::const_iterator gIt = groups.begin(); gIt != groups.end(); ++gIt ) { if ( !idMap.contains( *gIt ) ) continue; const NodeList& lst = idMap[*gIt]; - for ( NodeListIterator iter( lst ); iter.current(); ++iter ) + for ( NodeList::const_iterator iter = lst.begin(); iter != lst.end(); ++iter ) { + MenuNode* node = *iter; + if ( !node ) continue; + if ( rec ) - updateMenu( iter.current(), rec, false ); + updateMenu( node, rec, false ); - QAction* a = itemAction( iter.current()->id ); - if ( a ) - a->addTo( mw ); - else + MenuNode* par = node->parent; + if ( !isVisible( node->id, par ? par->id : -1 ) ) + continue; + + bool isMenu = false; + QAction* a = itemAction( node->id ); + if ( !a ) { - MenuAction* ma = menuAction( iter.current()->id ); - if ( ma && ma->popup() && ma->popup()->count() ) - ma->addTo( mw ); + isMenu = true; + a = menuAction( node->id ); } + if ( !a ) continue; + + if ( !isMenu || !a->menu()->isEmpty() || node->emptyEnabled > 0 ) + mw->addAction( a ); } } + // rebuild menu: 2. insert back all foreign actions + for( formapit = foreign.begin(); formapit != foreign.end(); ++formapit ) { + preva = formapit.key(); + foreach( a, formapit.value() ) + mw->insertAction( preva, a ); + } + + // remove extra separators simplifySeparators( mw ); - if ( updParent && node->parent && filled != checkWidget( mw ) ) + // update parent menu if necessary + if ( updParent && node->parent ) { updateMenu( node->parent, false ); + } } +/*! + \brief Internal update. + + Customizes the menu update processing. +*/ void QtxActionMenuMgr::internalUpdate() { - if ( isUpdatesEnabled() ) - updateMenu(); + if ( !isUpdatesEnabled() ) + return; + + updateMenu(); + myUpdateIds.clear(); } +/*! + \brief Check if action belongs to the menu manager + \internal + \param a action being checked + \param node parent menu node + \return \c true if action belongs to the menu \a node +*/ +bool QtxActionMenuMgr::ownAction( QAction* a, MenuNode* node ) const +{ + for ( NodeList::const_iterator iter = node->children.begin(); iter != node->children.end(); ++iter ) + { + QAction* mya = itemAction( (*iter)->id ); + if ( !mya ) + mya = menuAction( (*iter)->id ); + if ( mya && mya == a ) + return true; + } + return false; +} + +/*! + \brief Check if menu widget has any actions. + \param wid widget to be checked + \return \c true if widget contains action(s) +*/ bool QtxActionMenuMgr::checkWidget( QWidget* wid ) const { if ( !wid ) return false; - QMenuData* md = 0; - if ( wid->inherits( "QPopupMenu" ) ) - md = (QPopupMenu*)wid; - else if ( wid->inherits( "QMenuBar" ) ) - md = (QMenuBar*)wid; - - return md->count(); + bool res = false; + QList lst = wid->actions(); + for ( QList::const_iterator it = lst.begin(); it != lst.end() && !res; ++it ) { + res = !(*it)->isSeparator() && (*it)->isVisible(); + } + return res; } -QWidget* QtxActionMenuMgr::menuWidget( MenuNode* node) const +/*! + \brief Get menu widget for the given \a node. + \param node menu node + \return popup menu or main menu corresponding to the menu node + (or 0 if it is not found) +*/ +QWidget* QtxActionMenuMgr::menuWidget( MenuNode* node ) const { - if ( !node || node == &myRoot ) + if ( !node || node == myRoot ) return myMenu; if ( !myMenus.contains( node->id ) || !myMenus[node->id] ) return 0; - return myMenus[node->id]->popup(); + return myMenus[node->id]->menu(); } +/*! + \brief Remove extra separators from menu widget. + \param wid menu widget to be processed +*/ void QtxActionMenuMgr::simplifySeparators( QWidget* wid ) { - if ( wid && wid->inherits( "QPopupMenu" ) ) - Qtx::simplifySeparators( (QPopupMenu*)wid, false ); + Qtx::simplifySeparators( wid, false ); } +/*! + \brief Remove special symbols (&) from string to get clear menu title. + \param txt string to be processed + \return clear title +*/ QString QtxActionMenuMgr::clearTitle( const QString& txt ) const { QString res = txt; @@ -551,6 +1070,12 @@ QString QtxActionMenuMgr::clearTitle( const QString& txt ) const return res; } +/*! + \brief Create and inserts menu item recursively. + \param lst list of menu names + \param pId parent menu item ID + \return created menu item ID (last in the chain) +*/ int QtxActionMenuMgr::createMenu( const QStringList& lst, const int pId ) { if ( lst.isEmpty() ) @@ -558,37 +1083,204 @@ int QtxActionMenuMgr::createMenu( const QStringList& lst, const int pId ) QStringList sl( lst ); - QString title = sl.last().stripWhiteSpace(); - sl.remove( sl.fromLast() ); + QString title = sl.last().trimmed(); + sl.removeLast(); int parentId = sl.isEmpty() ? pId : createMenu( sl, pId ); return insert( title, parentId, -1 ); } +/*! + \brief Load actions description from the file. + \param fname file name + \param r action reader + \return \c true on success and \c false on error +*/ bool QtxActionMenuMgr::load( const QString& fname, QtxActionMgr::Reader& r ) { MenuCreator cr( &r, this ); return r.read( fname, cr ); } +/*! + \brief Check if the parent menu contains menu item with given \a title. + \param title menu title + \param pid parent menu item ID + \return \c true if parent menu item contains such child item +*/ +bool QtxActionMenuMgr::containsMenu( const QString& title, const int pid, const bool rec ) const +{ + return (bool)find( title, pid, rec ); +} + +/*! + \brief Check if the parent menu contains menu item with given \a id. + \param id menu item ID + \param pid parent menu item ID + \return \c true if parent menu item contains such child item +*/ +bool QtxActionMenuMgr::containsMenu( const int id, const int pid, const bool rec ) const +{ + return (bool)find( id, pid, rec ); +} + +/*! + \brief Get menu by the specified identifier. + \param id menu item ID + \return menu pointer or 0 if menu is not found +*/ +QMenu* QtxActionMenuMgr::findMenu( const int id ) const +{ + QMenu* m = 0; + QAction* a = menuAction( id ); + if ( a ) + m = a->menu(); + return m; +} + +/*! + \brief Get menu by the title. + \param title menu text + \param pid parent menu item ID (to start search) + \param rec if \c true, perform recursive update + \return menu pointer or 0 if menu is not found +*/ +QMenu* QtxActionMenuMgr::findMenu( const QString& title, const int pid, const bool rec ) const +{ + QMenu* m = 0; + MenuNode* node = find( title, pid, rec ); + if ( node ) + { + QAction* a = menuAction( node->id ); + if ( a ) + m = a->menu(); + } + return m; +} /*! - Class: QtxActionMenuMgr::MenuCreator - Level: Public + \brief Check if empty menu is enabled + \param id menu item ID + \return \c true if empty menu is enabled */ +bool QtxActionMenuMgr::isEmptyEnabled( const int id ) const +{ + MenuNode* node = find( id ); + if ( node && menuAction( id ) ) + return node->emptyEnabled > 0; + + return false; +} -QtxActionMenuMgr::MenuCreator::MenuCreator( QtxActionMgr::Reader* r, - QtxActionMenuMgr* mgr ) +/*! + \brief Enable/disable empty menu + \param id menu item ID + \param enable if \c true, empty menu will be enabled, otherwise empty menu will be disabled +*/ +void QtxActionMenuMgr::setEmptyEnabled( const int id, const bool enable ) +{ + MenuNode* node = find( id ); + if ( node && menuAction( id ) ) { + int old = node->emptyEnabled; + node->emptyEnabled += enable ? 1 : -1; + if ( ( old <= 0 && enable ) || ( old > 0 && !enable ) ) // update menu only if enabled state has been changed + updateMenu( node, true, true ); + } +} + +/*! + \brief Perform delayed menu update. + \param id menu item ID + \param rec if \c true, perform recursive update +*/ +void QtxActionMenuMgr::triggerUpdate( const int id, const bool rec ) +{ + bool isRec = rec; + if ( myUpdateIds.contains( id ) ) + isRec = isRec || myUpdateIds[ id ]; + myUpdateIds.insert( id, isRec ); + + QtxActionMgr::triggerUpdate(); +} + +/*! + \brief Called when action is changed. + + Schedule delayed update for parent menu of changed action. +*/ +void QtxActionMenuMgr::actionChanged( int id ) +{ + NodeList aNodes; + find( id, aNodes ); + + for ( NodeList::iterator it = aNodes.begin(); it != aNodes.end(); ++it ) + { + MenuNode* node = *it; + if ( node->visible ) { + triggerUpdate( node->parent ? node->parent->id : myRoot->id, false ); + } + } +} + +/*! + \brief Called when delayed content update is performed. + + Customizes the content update operation. +*/ +void QtxActionMenuMgr::updateContent() +{ + // Warning: For correct updating it is necessary to update the most enclosed submenu in first turn + // because not updated empty submenu will be skipped. Now the submenus are iterated in + // ascending order according to their identifiers. For a submenus with automatically generated + // identifiers this will work correctly since the uppermost submenus have the biggest number + // (identifiers are generated by decrementing 1 starting from -1). In general, if any submenu + // have positive identifiers this method might not work correctly. In this case it would be + // necessary to improve this method and to add preliminary sorting a submenus by depth of an + // enclosure. + for ( QMap::const_iterator it = myUpdateIds.constBegin(); it != myUpdateIds.constEnd(); ++it ) + { + MenuNode* node = it.key() == -1 ? myRoot : find( it.key() ); + if ( node ) + updateMenu( node, it.value() ); + } + myUpdateIds.clear(); +} + +/*! + \class QtxActionMenuMgr::MenuCreator + \brief Menu actions creator. + + Used by Reader to create actions by reading descriptions from the file + and fill in the action manager with the actions. +*/ + +/*! + \brief Constructor. + \param r menu actions reader + \param mgr menu manager +*/ +QtxActionMenuMgr::MenuCreator::MenuCreator( QtxActionMgr::Reader* r, QtxActionMenuMgr* mgr ) : QtxActionMgr::Creator( r ), myMgr( mgr ) { } +/*! + \brief Destructor. +*/ QtxActionMenuMgr::MenuCreator::~MenuCreator() { } +/*! + \brief Create and append to the action manager a new action. + \param tag item tag name + \param subMenu \c true if this item is submenu + \param attr attributes map + \param pId parent action ID + \return menu action ID +*/ int QtxActionMenuMgr::MenuCreator::append( const QString& tag, const bool subMenu, const ItemAttributes& attr, const int pId ) { @@ -613,20 +1305,21 @@ int QtxActionMenuMgr::MenuCreator::append( const QString& tag, const bool subMen res = myMgr->insert( separator(), pId, intValue( attr, group, 0 ), intValue( attr, pos, -1 ) ); else { - QPixmap pix; QIconSet set; + QIcon set; + QPixmap pix; QString name = strValue( attr, icon ); if( !name.isEmpty() && loadPixmap( name, pix ) ) - set = QIconSet( pix ); + set = QIcon( pix ); QtxAction* newAct = new QtxAction( strValue( attr, tooltip ), set, - strValue( attr, label ), + strValue( attr, label ), QKeySequence( strValue( attr, accel ) ), myMgr ); newAct->setToolTip( strValue( attr, tooltip ) ); QString toggleact = strValue( attr, toggle ); - newAct->setToggleAction( !toggleact.isEmpty() ); - newAct->setOn( toggleact.lower()=="true" ); - + newAct->setCheckable( !toggleact.isEmpty() ); + newAct->setChecked( toggleact.toLower() == "true" ); + connect( newAct ); int aid = myMgr->registerAction( newAct, actId ); res = myMgr->insert( aid, pId, intValue( attr, group, 0 ), intValue( attr, pos, -1 ) );