diff options
Diffstat (limited to 'kdevdesigner/designer/popupmenueditor.cpp')
-rw-r--r-- | kdevdesigner/designer/popupmenueditor.cpp | 1469 |
1 files changed, 1469 insertions, 0 deletions
diff --git a/kdevdesigner/designer/popupmenueditor.cpp b/kdevdesigner/designer/popupmenueditor.cpp new file mode 100644 index 00000000..198880f9 --- /dev/null +++ b/kdevdesigner/designer/popupmenueditor.cpp @@ -0,0 +1,1469 @@ +/********************************************************************** +** Copyright (C) 2003 Trolltech AS. All rights reserved. +** +** This file is part of Qt Designer. +** +** This file may be distributed and/or modified under the terms of the +** GNU General Public License version 2 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. +** +** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition +** licenses may use this file in accordance with the Qt Commercial License +** Agreement provided with the Software. +** +** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +** See http://www.trolltech.com/gpl/ for GPL licensing information. +** See http://www.trolltech.com/pricing.html or email [email protected] for +** information about Qt Commercial License Agreements. +** +** Contact [email protected] if any conditions of this licensing are +** not clear to you. +** +**********************************************************************/ + +#include <qapplication.h> +#include <qcstring.h> +#include <qdatastream.h> +#include <qdragobject.h> +#include <qlineedit.h> +#include <qobjectlist.h> +#include <qpainter.h> +#include <qpopupmenu.h> +#include <qrect.h> +#include <qsize.h> +#include <qstyle.h> +#include <qtimer.h> +#include "actiondnd.h" +#include "actioneditorimpl.h" +#include "command.h" +#include "formfile.h" +#include "formwindow.h" +#include "mainwindow.h" +#include "metadatabase.h" +#include "pixmapchooser.h" +#include "popupmenueditor.h" +#include "menubareditor.h" + +#include <klocale.h> + +// Drag Object Declaration ------------------------------------------- + +class PopupMenuEditorItemPtrDrag : public QStoredDrag +{ +public: + PopupMenuEditorItemPtrDrag( PopupMenuEditorItem * item, QWidget * parent = 0, + const char * name = 0 ); + ~PopupMenuEditorItemPtrDrag() {}; + static bool canDecode( QDragMoveEvent * e ); + static bool decode( QDropEvent * e, PopupMenuEditorItem ** i ); +}; + +// Drag Object Implementation --------------------------------------- + +PopupMenuEditorItemPtrDrag::PopupMenuEditorItemPtrDrag( PopupMenuEditorItem * item, + QWidget * parent, const char * name ) + : QStoredDrag( "qt/popupmenueditoritemptr", parent, name ) +{ + QByteArray data( sizeof( Q_LONG ) ); + QDataStream stream( data, IO_WriteOnly ); + stream << ( Q_LONG ) item; + setEncodedData( data ); +} + +bool PopupMenuEditorItemPtrDrag::canDecode( QDragMoveEvent * e ) +{ + return e->provides( "qt/popupmenueditoritemptr" ); +} + +bool PopupMenuEditorItemPtrDrag::decode( QDropEvent * e, PopupMenuEditorItem ** i ) +{ + QByteArray data = e->encodedData( "qt/popupmenueditoritemptr" ); + QDataStream stream( data, IO_ReadOnly ); + + if ( !data.size() ) + return FALSE; + + Q_LONG p = 0; + stream >> p; + *i = ( PopupMenuEditorItem *) p; + + return TRUE; +} + +// PopupMenuEditorItem Implementation ----------------------------------- + +PopupMenuEditorItem::PopupMenuEditorItem( PopupMenuEditor * menu, QObject * parent, const char * name ) + : QObject( parent, name ), + a( 0 ), + s( 0 ), + m( menu ), + separator( FALSE ), + removable( FALSE ) +{ + init(); + a = new QAction( this ); + QObject::connect( a, SIGNAL( destroyed() ), this, SLOT( selfDestruct() ) ); +} + + +PopupMenuEditorItem::PopupMenuEditorItem( QAction * action, PopupMenuEditor * menu, + QObject * parent, const char * name ) + : QObject( parent, name ), + a( action ), + s( 0 ), + m( menu ), + separator( FALSE ), + removable( TRUE ) +{ + init(); + if ( /*a->name() == "qt_separator_action" ||*/ ::qt_cast<QSeparatorAction*>(a) ) + separator = TRUE; + if ( a && a->children() ) + a->installEventFilter( this ); +} + +PopupMenuEditorItem::PopupMenuEditorItem( PopupMenuEditorItem * item, PopupMenuEditor * menu, + QObject * parent, const char * name ) + : QObject( parent, name ), + a( item->a ), + s( 0 ), + m( menu ), + separator( item->separator ), + removable( item->removable ) +{ + init(); + if ( ::qt_cast<QActionGroup*>(a) ) + a->installEventFilter( this ); +} + +PopupMenuEditorItem::~PopupMenuEditorItem() +{ + +} + +void PopupMenuEditorItem::init() +{ + if ( a ) { + QObject::connect( a, SIGNAL( destroyed() ), this, SLOT( selfDestruct() ) ); + if ( m && !isSeparator() ) { + s = new PopupMenuEditor( m->formWindow(), m ); + QString n = "PopupMenuEditor"; + m->formWindow()->unify( s, n, TRUE ); + s->setName( n ); + MetaDataBase::addEntry( s ); + } + } +} + +PopupMenuEditorItem::ItemType PopupMenuEditorItem::type() const +{ + if ( separator ) + return Separator; + else if ( a ) + return Action; + return Unknown; +} + +void PopupMenuEditorItem::setVisible( bool enable ) +{ + if ( a ) + a->setVisible( enable ); +} + +bool PopupMenuEditorItem::isVisible() const +{ + QActionGroup *g = ::qt_cast<QActionGroup*>(a); + if ( g ) + return ( g->isVisible() && g->usesDropDown() ); + else if ( a ) + return a->isVisible(); + return FALSE; +} + +void PopupMenuEditorItem::showMenu( int x, int y ) +{ + if ( ( !separator ) && s ) { + s->move( x, y ); + s->show(); + s->raise(); + } +} + +void PopupMenuEditorItem::hideMenu() +{ + if ( s ) { + s->hideSubMenu(); + s->hide(); + } +} + +void PopupMenuEditorItem::focusOnMenu() +{ + if ( s ) { + s->showSubMenu(); + s->setFocus(); + } +} + +int PopupMenuEditorItem::count() const +{ + if ( s ) { + return s->count(); + } else if ( ::qt_cast<QActionGroup*>(a) ) { + const QObjectList * l = a->children(); + if ( l ) + return l->count(); + } + return 0; +} + +bool PopupMenuEditorItem::eventFilter( QObject * o, QEvent * event ) +{ + if ( ! ::qt_cast<QActionGroup*>( o ) ) + return FALSE; + if ( event->type() == QEvent::ChildInserted ) { + QChildEvent * ce = ( QChildEvent * ) event; + QObject * c = ce->child(); + QAction * action = ::qt_cast<QAction*>( c ); + if ( s->find( action ) != -1 ) // avoid duplicates + return FALSE; + QActionGroup * actionGroup = ::qt_cast<QActionGroup*>( c ); + if ( actionGroup ) + s->insert( actionGroup ); + else if ( action ) + s->insert( action ); + } + return FALSE; +} + +void PopupMenuEditorItem::selfDestruct() +{ + hideMenu(); + int i = m->find( s ); + if ( i != -1 && i < m->count() ) + m->remove( i ); // remove this item + a = 0; // the selfDestruct call was caused by the deletion of the action + delete this; +} + +// PopupMenuEditor Implementation ----------------------------------- + +PopupMenuEditorItem * PopupMenuEditor::draggedItem = 0; +int PopupMenuEditor::clipboardOperation = 0; +PopupMenuEditorItem * PopupMenuEditor::clipboardItem = 0; + +PopupMenuEditor::PopupMenuEditor( FormWindow * fw, QWidget * parent, const char * name ) + : QWidget( 0, name, WStyle_Customize | WStyle_NoBorder | WRepaintNoErase | WResizeNoErase ), + formWnd( fw ), + parentMenu( parent ), + iconWidth( 0 ), + textWidth( 0 ), + accelWidth( 0 ), + arrowWidth( 30 ), + borderSize( 2 ), + currentField( 1 ), + currentIndex( 0 ) +{ + init(); +} + +PopupMenuEditor::PopupMenuEditor( FormWindow * fw, PopupMenuEditor * menu, + QWidget * parent, const char * name ) + : QWidget( 0, name, WStyle_Customize | WStyle_NoBorder | WRepaintNoErase ), + formWnd( fw ), + parentMenu( parent ), + iconWidth( menu->iconWidth ), + textWidth( menu->textWidth ), + accelWidth( menu->accelWidth ), + arrowWidth( menu->arrowWidth ), + borderSize( menu->borderSize ), + currentField( menu->currentField ), + currentIndex( menu->currentIndex ) +{ + init(); + PopupMenuEditorItem * i; + for ( i = menu->itemList.first(); i; i = menu->itemList.next() ) { + PopupMenuEditorItem * n = new PopupMenuEditorItem( i, this ); + itemList.append( n ); + } +} + +PopupMenuEditor::~PopupMenuEditor() +{ + itemList.setAutoDelete( TRUE ); +} + +void PopupMenuEditor::init() +{ + reparent( ( QMainWindow * ) formWnd->mainContainer(), pos() ); + + addItem.action()->setMenuText( i18n("new item") ); + addSeparator.action()->setMenuText( i18n("new separator") ); + + setAcceptDrops( TRUE ); + setFocusPolicy( StrongFocus ); + + lineEdit = new QLineEdit( this ); + lineEdit->hide(); + lineEdit->setFrameStyle(QFrame::Plain | QFrame::NoFrame); + lineEdit->polish(); + lineEdit->setBackgroundOrigin(ParentOrigin); + lineEdit->setBackgroundMode(PaletteButton); + lineEdit->installEventFilter( this ); + + dropLine = new QWidget( this, 0, Qt::WStyle_NoBorder | WStyle_StaysOnTop ); + dropLine->setBackgroundColor( Qt::red ); + dropLine->hide(); + + hide(); +} + +void PopupMenuEditor::insert( PopupMenuEditorItem * item, int index ) +{ + if ( !item ) + return; + if ( index == -1 ) { + itemList.append( item ); + if ( isVisible() ) + currentIndex = itemList.count() - 1; + } else { + itemList.insert( index, item ); + if ( isVisible() ) + currentIndex = index; + } + item->m = this; + item->s->parentMenu = this; + resizeToContents(); + if ( isVisible() && parentMenu ) + parentMenu->update(); // draw arrow in parent menu + emit inserted( item->action() ); +} + +void PopupMenuEditor::insert( QAction * action, int index ) +{ + if ( !action ) + return; + insert( new PopupMenuEditorItem( action, this, 0, action->name() ), index ); +} + +void PopupMenuEditor::insert( QActionGroup * actionGroup, int index ) +{ + if ( !actionGroup ) + return; + bool dropdown = actionGroup->usesDropDown(); + PopupMenuEditorItem *i = new PopupMenuEditorItem( (QAction *)actionGroup, this, 0, + QString( actionGroup->name() ) + "Menu" ); + QActionGroup *g = 0; + QObjectList *l = actionGroup->queryList( "QAction", 0, FALSE, FALSE ); + QObjectListIterator it( *l ); + insert( i, index ); + for ( ; it.current(); ++it ) { + g = ::qt_cast<QActionGroup*>(it.current()); + if ( g ) { + if ( dropdown ) + i->s->insert( g ); + else + insert( g ); + } else { + i->s->insert( (QAction*)it.current() ); + } + } + delete l; +} + +int PopupMenuEditor::find( const QAction * action ) +{ + PopupMenuEditorItem * i = itemList.first(); + while ( i ) { + if ( i->action() == action ) + return itemList.at(); + i = itemList.next(); + } + return -1; +} + +int PopupMenuEditor::find( PopupMenuEditor * menu ) +{ + PopupMenuEditorItem * i = itemList.first(); + while ( i ) { + if ( i->subMenu() == menu ) + return itemList.at(); + i = itemList.next(); + } + return -1; +} + +int PopupMenuEditor::count() +{ + return itemList.count(); +} + +PopupMenuEditorItem * PopupMenuEditor::at( int index ) +{ + return itemList.at( index ); +} + +void PopupMenuEditor::exchange( int a, int b ) +{ + PopupMenuEditorItem * ia = itemList.at( a ); + PopupMenuEditorItem * ib = itemList.at( b ); + if ( !ia || !ib || + ia == &addItem || ia == &addSeparator || + ib == &addItem || ib == &addSeparator ) + return; // do nothing + itemList.replace( b, ia ); + itemList.replace( a, ib ); +} + +void PopupMenuEditor::cut( int index ) +{ + int idx = ( index == -1 ? currentIndex : index ); + + if ( clipboardItem && clipboardOperation == Cut ) + delete clipboardItem; + + clipboardOperation = Cut; + clipboardItem = itemList.at( idx ); + + if ( clipboardItem == &addItem || clipboardItem == &addSeparator ) { + clipboardOperation = None; + clipboardItem = 0; + return; // do nothing + } + + RemoveActionFromPopupCommand * cmd = + new RemoveActionFromPopupCommand( i18n( "Cut Item" ), formWnd, this, idx ); + formWnd->commandHistory()->addCommand( cmd ); + cmd->execute(); +} + +void PopupMenuEditor::copy( int index ) +{ + int idx = ( index == -1 ? currentIndex : index ); + + if ( clipboardItem && clipboardOperation == Cut ) + delete clipboardItem; + + clipboardOperation = Copy; + clipboardItem = itemList.at( idx ); + + if ( clipboardItem == &addItem || clipboardItem == &addSeparator ) { + clipboardOperation = None; + clipboardItem = 0; + } +} + +void PopupMenuEditor::paste( int index ) +{ + int idx = ( index == -1 ? currentIndex : index ); + + if ( clipboardItem && clipboardOperation ) { + PopupMenuEditorItem * n = new PopupMenuEditorItem( clipboardItem, this ); + AddActionToPopupCommand * cmd = + new AddActionToPopupCommand( i18n( "Paste Item" ), formWnd, this, n, idx ); + formWnd->commandHistory()->addCommand( cmd ); + cmd->execute(); + } +} + +void PopupMenuEditor::insertedActions( QPtrList<QAction> & list ) +{ + QAction * a = 0; + PopupMenuEditorItem * i = itemList.first(); + + while ( i ) { + a = i->action(); + if ( a ) + list.append( a ); + i = itemList.next(); + } +} + +void PopupMenuEditor::show() +{ + resizeToContents(); + QWidget::show(); +} + +void PopupMenuEditor::choosePixmap( int index ) +{ + int idx = ( index == -1 ? currentIndex : index ); + + PopupMenuEditorItem * i = 0; + QAction * a = 0; + + if ( idx < (int)itemList.count() ) { + i = itemList.at( idx ); + a = i->action(); + } else { + createItem(); + } + + hide(); // qChoosePixmap hides the menu + QIconSet icons( qChoosePixmap( 0, formWnd, 0, 0 ) ); + SetActionIconsCommand * cmd = + new SetActionIconsCommand( i18n( "Set Icon" ), formWnd, a, this, icons ); + formWnd->commandHistory()->addCommand( cmd ); + cmd->execute(); + show(); + setFocus(); +} + +void PopupMenuEditor::showLineEdit( int index ) +{ + int idx = ( index == -1 ? currentIndex : index ); + + PopupMenuEditorItem * i = 0; + + if ( idx >= (int)itemList.count() ) + i = &addItem; + else + i = itemList.at( idx ); + + // open edit currentField for item name + lineEdit->setText( i->action()->menuText() ); + lineEdit->selectAll(); + lineEdit->setGeometry( borderSize + iconWidth, borderSize + itemPos( i ), + textWidth, itemHeight( i ) ); + lineEdit->show(); + lineEdit->setFocus(); +} + +void PopupMenuEditor::setAccelerator( int key, Qt::ButtonState state, int index ) +{ + // FIXME: make this a command + + int idx = ( index == -1 ? currentIndex : index ); + + if ( key == Qt::Key_Shift || + key == Qt::Key_Control || + key == Qt::Key_Alt || + key == Qt::Key_Meta || + key == Qt::Key_unknown ) + return; // ignore these keys when they are pressed + + PopupMenuEditorItem * i = 0; + + if ( idx >= (int)itemList.count() ) + i = createItem(); + else + i = itemList.at( idx ); + + int shift = ( state & Qt::ShiftButton ? Qt::SHIFT : 0 ); + int ctrl = ( state & Qt::ControlButton ? Qt::CTRL : 0 ); + int alt = ( state & Qt::AltButton ? Qt::ALT : 0 ); + int meta = ( state & Qt::MetaButton ? Qt::META : 0 ); + + QAction * a = i->action(); + QKeySequence ks = a->accel(); + int keys[4] = { ks[0], ks[1], ks[2], ks[3] }; + int n = 0; + while ( n < 4 && ks[n++] ); + n--; + if ( n < 4 ) + keys[n] = key | shift | ctrl | alt | meta; + a->setAccel( QKeySequence( keys[0], keys[1], keys[2], keys[3] ) ); + MetaDataBase::setPropertyChanged( a, "accel", TRUE ); + resizeToContents(); +} + +void PopupMenuEditor::resizeToContents() +{ + QSize s = contentsSize(); + dropLine->resize( s.width(), 2 ); + s.rwidth() += borderSize * 2; + s.rheight() += borderSize * 2; + resize( s ); +} + +void PopupMenuEditor::showSubMenu() +{ + if ( currentIndex < (int)itemList.count() ) { + itemList.at( currentIndex )->showMenu( pos().x() + width() - borderSize * 3, + pos().y() + itemPos( at( currentIndex ) ) + + borderSize * 2 ); + setFocus(); // Keep focus in this widget + } +} + +void PopupMenuEditor::hideSubMenu() +{ + if ( currentIndex < (int)itemList.count() ) + itemList.at( currentIndex )->hideMenu(); +} + +void PopupMenuEditor::focusOnSubMenu() +{ + if ( currentIndex < (int)itemList.count() ) + itemList.at( currentIndex )->focusOnMenu(); +} + +// This function has no undo. It is only here to remove an item when its action was +// removed from the action editor. +// Use removeItem to put the command on the command stack. +void PopupMenuEditor::remove( int index ) +{ + int idx = ( index == -1 ? currentIndex : index ); + PopupMenuEditorItem * i = itemList.at( idx ); + if ( i && i->isRemovable() ) { + itemList.remove( idx ); + int n = itemList.count() + 1; + if ( currentIndex >= n ) + currentIndex = itemList.count() + 1; + emit removed( i->action() ); + resizeToContents(); + } +} + +PopupMenuEditorItem * PopupMenuEditor::createItem( QAction * a ) +{ + ActionEditor * ae = (ActionEditor *) formWindow()->mainWindow()->child( 0, "ActionEditor" ); + if ( !a ) + a = ae->newActionEx(); + PopupMenuEditorItem * i = new PopupMenuEditorItem( a, this ); + QString n = QString( a->name() ) + "Item"; + formWindow()->unify( i, n, FALSE ); + i->setName( n ); + AddActionToPopupCommand * cmd = + new AddActionToPopupCommand( i18n( "Add Item" ), formWnd, this, i ); + formWnd->commandHistory()->addCommand( cmd ); + cmd->execute(); + return i; +} + +void PopupMenuEditor::removeItem( int index ) +{ + int idx = ( index == -1 ? currentIndex : index ); + if ( idx < (int)itemList.count() ) { + RemoveActionFromPopupCommand * cmd = new RemoveActionFromPopupCommand( i18n( "Remove Item" ), + formWnd, + this, + idx ); + formWnd->commandHistory()->addCommand( cmd ); + cmd->execute(); + if ( itemList.count() == 0 && parentMenu ) + parentMenu->update(); + resizeToContents(); + } +} + +PopupMenuEditorItem * PopupMenuEditor::currentItem() +{ + int count = itemList.count(); + if ( currentIndex < count ) + return itemList.at( currentIndex ); + else if ( currentIndex == count ) + return &addItem; + return &addSeparator; +} + +PopupMenuEditorItem * PopupMenuEditor::itemAt( int y ) +{ + PopupMenuEditorItem * i = itemList.first(); + int iy = 0; + + while ( i ) { + iy += itemHeight( i ); + if ( iy > y ) + return i; + i = itemList.next(); + } + iy += itemHeight( &addItem ); + if ( iy > y ) + return &addItem; + return &addSeparator; +} + +void PopupMenuEditor::setFocusAt( const QPoint & pos ) +{ + hideSubMenu(); + lineEdit->hide(); + + currentIndex = 0; + int iy = 0; + PopupMenuEditorItem * i = itemList.first(); + + while ( i ) { + iy += itemHeight( i ); + if ( iy > pos.y() ) + break; + i = itemList.next(); + currentIndex++; + } + + iy += itemHeight( &addItem ); + if ( iy <= pos.y() ) + currentIndex++; + + if ( currentIndex < (int)itemList.count() ) { + if ( pos.x() < iconWidth ) + currentField = 0; + else if ( pos.x() < iconWidth + textWidth ) + currentField = 1; + else + currentField = 2; + } else { + currentField = 1; + } + + showSubMenu(); +} + +bool PopupMenuEditor::eventFilter( QObject * o, QEvent * e ) +{ + if ( o == lineEdit && e->type() == QEvent::FocusOut ) { + leaveEditMode( 0 ); + update(); + } + return QWidget::eventFilter( o, e ); +} + +void PopupMenuEditor::paintEvent( QPaintEvent * ) +{ + QPainter p( this ); + p.save(); + QRegion reg( rect() ); + QRegion mid( borderSize, borderSize, + rect().width() - borderSize * 2, rect().height() - borderSize * 2 ); + reg -= mid; + p.setClipRegion( reg ); + style().drawPrimitive( QStyle::PE_PanelPopup, &p, rect(), colorGroup() ); + p.restore(); + drawItems( &p ); +} + +void PopupMenuEditor::mousePressEvent( QMouseEvent * e ) +{ + mousePressPos = e->pos(); + setFocusAt( mousePressPos ); + e->accept(); + update(); +} + +void PopupMenuEditor::mouseDoubleClickEvent( QMouseEvent * ) +{ + setFocusAt( mousePressPos ); + if ( currentItem() == &addSeparator ) { + PopupMenuEditorItem * i = createItem( new QSeparatorAction( 0 ) ); + i->setSeparator( TRUE ); + return; + } + if ( currentField == 0 ) { + choosePixmap(); + resizeToContents(); + } else if ( currentField == 1 ) { + showLineEdit(); + } +} + +void PopupMenuEditor::mouseMoveEvent( QMouseEvent * e ) +{ + if ( e->state() & Qt::LeftButton ) { + if ( ( e->pos() - mousePressPos ).manhattanLength() > 3 ) { + draggedItem = itemAt( mousePressPos.y() ); + if ( draggedItem == &addItem ) { + draggedItem = createItem(); + RenameActionCommand cmd( i18n( "Rename Item" ), formWnd, draggedItem->action(), + this, "Unnamed" ); + cmd.execute(); + // FIXME: start rename after drop + } else if ( draggedItem == &addSeparator ) { + draggedItem = createItem( new QSeparatorAction( 0 ) ); + draggedItem->setSeparator( TRUE ); + } + + PopupMenuEditorItemPtrDrag * d = + new PopupMenuEditorItemPtrDrag( draggedItem, this ); + + hideSubMenu(); + + draggedItem->setVisible( FALSE ); + resizeToContents(); + + // If the item is dropped in the same list, + // we will have two instances of the same pointer + // in the list. We use node instead. + int idx = itemList.find( draggedItem ); + QLNode * node = itemList.currentNode(); + + d->dragCopy(); // dragevents and stuff happens + + if ( draggedItem ) { // item was not dropped + draggedItem->setVisible( TRUE ); + draggedItem = 0; + if ( hasFocus() ) { + hideSubMenu(); + resizeToContents(); + showSubMenu(); + } + } else { // item was dropped + itemList.takeNode( node )->setVisible( TRUE ); + if ( currentIndex > 0 && currentIndex > idx ) + --currentIndex; + // the drop might happen in another menu, so we'll resize + // and show the submenu there + } + } + } +} + +void PopupMenuEditor::dragEnterEvent( QDragEnterEvent * e ) +{ + if ( e->provides( "qt/popupmenueditoritemptr" ) || + e->provides( "application/x-designer-actions" ) || + e->provides( "application/x-designer-actiongroup" ) ) { + e->accept(); + dropLine->show(); + } +} + +void PopupMenuEditor::dragLeaveEvent( QDragLeaveEvent * ) +{ + dropLine->hide(); +} + +void PopupMenuEditor::dragMoveEvent( QDragMoveEvent * e ) +{ + QPoint pos = e->pos(); + dropLine->move( borderSize, snapToItem( pos.y() ) ); + + if ( currentItem() != itemAt( pos.y() ) ) { + hideSubMenu(); + setFocusAt( pos ); + showSubMenu(); + } +} + +void PopupMenuEditor::dropEvent( QDropEvent * e ) +{ + if ( !( e->provides( "qt/popupmenueditoritemptr" ) || + e->provides( "application/x-designer-actions" ) || + e->provides( "application/x-designer-actiongroup" ) ) ) + return; + + // Hide the sub menu of the current item, but do it later + if ( currentIndex < (int)itemList.count() ) { + PopupMenuEditor *s = itemList.at( currentIndex )->s; + QTimer::singleShot( 0, s, SLOT( hide() ) ); + } + + draggedItem = 0; + PopupMenuEditorItem * i = 0; + + if ( e->provides( "qt/popupmenueditoritemptr" ) ) { + PopupMenuEditorItemPtrDrag::decode( e, &i ); + } else { + if ( e->provides( "application/x-designer-actiongroup" ) ) { + QActionGroup * g = ::qt_cast<QDesignerActionGroup*>(ActionDrag::action()); + if ( g->usesDropDown() ) { + i = new PopupMenuEditorItem( g, this ); + QString n = QString( g->name() ) + "Item"; + formWindow()->unify( i, n, FALSE ); + i->setName( n ); + QObjectList *l = g->queryList( "QAction", 0, FALSE, FALSE ); + QObjectListIterator it( *l ); + for ( ; it.current(); ++it ) { + g = ::qt_cast<QActionGroup*>(it.current()); + if ( g ) + i->s->insert( g ); + else + i->s->insert( (QAction*)it.current() ); + } + delete l; + } else { + dropInPlace( g, e->pos().y() ); + } + } else if ( e->provides( "application/x-designer-actions" ) ) { + QAction *a = ::qt_cast<QDesignerAction*>(ActionDrag::action()); + i = new PopupMenuEditorItem( a, this ); + } + } + + if ( i ) { + dropInPlace( i, e->pos().y() ); + QTimer::singleShot( 0, this, SLOT( resizeToContents() ) ); + } + + QTimer::singleShot( 0, this, SLOT( showSubMenu() ) ); + QTimer::singleShot( 0, this, SLOT( setFocus() ) ); + dropLine->hide(); + e->accept(); +} + +void PopupMenuEditor::keyPressEvent( QKeyEvent * e ) +{ + if ( lineEdit->isHidden() ) { // In navigation mode + + switch ( e->key() ) { + + case Qt::Key_Delete: + hideSubMenu(); + removeItem(); + showSubMenu(); + break; + + case Qt::Key_Backspace: + clearCurrentField(); + break; + + case Qt::Key_Up: + navigateUp( e->state() & Qt::ControlButton ); + break; + + case Qt::Key_Down: + navigateDown( e->state() & Qt::ControlButton ); + break; + + case Qt::Key_Left: + navigateLeft(); + break; + + case Qt::Key_Right: + navigateRight(); + break; + + case Qt::Key_PageUp: + currentIndex = 0; + break; + + case Qt::Key_PageDown: + currentIndex = itemList.count(); + break; + + case Qt::Key_Enter: + case Qt::Key_Return: + case Qt::Key_F2: + enterEditMode( e ); + // move on + case Qt::Key_Alt: + case Qt::Key_Shift: + case Qt::Key_Control: + // do nothing + return; + + case Qt::Key_Escape: + currentField = 0; + navigateLeft(); + break; + + case Qt::Key_C: + if ( e->state() & Qt::ControlButton && + currentIndex < (int)itemList.count() ) { + copy( currentIndex ); + break; + } + + case Qt::Key_X: + if ( e->state() & Qt::ControlButton && + currentIndex < (int)itemList.count() ) { + hideSubMenu(); + cut( currentIndex ); + showSubMenu(); + break; + } + + case Qt::Key_V: + if ( e->state() & Qt::ControlButton ) { + hideSubMenu(); + paste( currentIndex < (int)itemList.count() ? currentIndex + 1: itemList.count() ); + showSubMenu(); + break; + } + + default: + if ( currentItem()->isSeparator() ) + return; + if ( currentField == 1 ) { + showLineEdit(); + QApplication::sendEvent( lineEdit, e ); + e->accept(); + return; + } else if ( currentField == 2 ) { + setAccelerator( e->key(), e->state() ); + showSubMenu(); + } + break; + + } + + } else { // In edit mode + switch ( e->key() ) { + case Qt::Key_Enter: + case Qt::Key_Return: + case Qt::Key_Escape: + leaveEditMode( e ); + e->accept(); + return; + } + } + update(); +} + +void PopupMenuEditor::focusInEvent( QFocusEvent * ) +{ + showSubMenu(); + update(); + parentMenu->update(); +} + +void PopupMenuEditor::focusOutEvent( QFocusEvent * ) +{ + QWidget * fw = qApp->focusWidget(); + if ( !fw || ( !::qt_cast<PopupMenuEditor*>(fw) && fw != lineEdit ) ) { + hideSubMenu(); + if ( fw && ::qt_cast<MenuBarEditor*>(fw) ) + return; + QWidget * w = this; + while ( w && w != fw && ::qt_cast<PopupMenuEditor*>(w) ) { // hide all popups + w->hide(); + w = ((PopupMenuEditor *)w)->parentEditor(); + } + } +} + +void PopupMenuEditor::drawItem( QPainter * p, PopupMenuEditorItem * i, + const QRect & r, int f ) const +{ + int x = r.x(); + int y = r.y(); + int h = r.height(); + + p->fillRect( r, colorGroup().brush( QColorGroup::Background ) ); + + if ( i->isSeparator() ) { + style().drawPrimitive( QStyle::PE_Separator, p, + QRect( r.x(), r.y() + 2, r.width(), 1 ), + colorGroup(), QStyle::Style_Sunken | f ); + return; + } + + const QAction * a = i->action(); + if ( a->isToggleAction() && a->isOn() ) { + style().drawPrimitive( QStyle::PE_CheckMark, p, + QRect( x , y, iconWidth, h ), + colorGroup(), f ); + } else { + QPixmap icon = a->iconSet().pixmap( QIconSet::Automatic, QIconSet::Normal ); + p->drawPixmap( x + ( iconWidth - icon.width() ) / 2, + y + ( h - icon.height() ) / 2, + icon ); + } + x += iconWidth; + p->drawText( x, y, textWidth, h, + QPainter::AlignLeft | + QPainter::AlignVCenter | + Qt::ShowPrefix | + Qt::SingleLine, + a->menuText() ); + + x += textWidth + borderSize * 3; + p->drawText( x, y, accelWidth, h, + QPainter::AlignLeft | QPainter::AlignVCenter, + a->accel() ); + if ( i->count() ) // Item has submenu + style().drawPrimitive( QStyle::PE_ArrowRight, p, + QRect( r.width() - arrowWidth, r.y(), arrowWidth, r.height() ), + colorGroup(), f ); +} + +void PopupMenuEditor::drawWinFocusRect( QPainter * p, const QRect & r ) const +{ + if ( currentIndex < (int)itemList.count() && + ((PopupMenuEditor*)this)->itemList.at( currentIndex )->isSeparator() ) { + p->drawWinFocusRect( borderSize, r.y(), width() - borderSize * 2, r.height() ); + return; + } + int y = r.y(); + int h = r.height(); + if ( currentField == 0 ) + p->drawWinFocusRect( borderSize + 1, y, iconWidth - 2, h ); + else if ( currentField == 1 ) + p->drawWinFocusRect( borderSize + iconWidth, y, textWidth, h ); + else if ( currentField == 2 ) + p->drawWinFocusRect( borderSize + iconWidth + textWidth + + borderSize * 3, y, accelWidth, h ); +} + +void PopupMenuEditor::drawItems( QPainter * p ) +{ + int flags = 0; + int idx = 0; + + QColorGroup enabled = colorGroup(); + QColorGroup disabled = palette().disabled(); + QRect focus; + QRect rect( borderSize, borderSize, width() - borderSize * 2, 0 ); + + PopupMenuEditorItem * i = itemList.first(); + while ( i ) { + if ( i->isVisible() ) { + rect.setHeight( itemHeight( i ) ); + if ( idx == currentIndex ) + focus = rect; + if ( i->action()->isEnabled() ) { + flags = QStyle::Style_Enabled; + p->setPen( enabled.buttonText() ); + } else { + flags = QStyle::Style_Default; + p->setPen( disabled.buttonText() ); + } + drawItem( p, i, rect, flags ); + rect.moveBy( 0, rect.height() ); + } + i = itemList.next(); + idx++; + } + + // Draw the "add item" and "add separator" items + p->setPen( darkBlue ); + rect.setHeight( itemHeight( &addItem ) ); + if ( idx == currentIndex ) + focus = rect; + drawItem( p, &addItem, rect, QStyle::Style_Default ); + rect.moveBy( 0, rect.height() ); + idx++; + rect.setHeight( itemHeight( &addSeparator ) ); + if ( idx == currentIndex ) + focus = rect; + drawItem( p, &addSeparator, rect, QStyle::Style_Default ); + idx++; + + if ( hasFocus() && !draggedItem ) + drawWinFocusRect( p, focus ); +} + +QSize PopupMenuEditor::contentsSize() +{ + QRect textRect = fontMetrics().boundingRect( addSeparator.action()->menuText() ); + textWidth = textRect.width(); + accelWidth = textRect.height(); // default size + iconWidth = textRect.height(); + + int w = 0; + int h = itemHeight( &addItem ) + itemHeight( &addSeparator ); + PopupMenuEditorItem * i = itemList.first(); + QAction * a = 0; + while ( i ) { + if ( i->isVisible() ) { + if ( !i->isSeparator() ) { + a = i->action(); + w = a->iconSet().pixmap( QIconSet::Automatic, QIconSet::Normal ).rect().width() + + borderSize; // padding + iconWidth = QMAX( iconWidth, w ); + w = fontMetrics().boundingRect( a->menuText() ).width(); + textWidth = QMAX( textWidth, w ); + w = fontMetrics().boundingRect( a->accel() ).width() + 2; // added padding? + accelWidth = QMAX( accelWidth, w ); + } + h += itemHeight( i ); + } + i = itemList.next(); + } + + int width = iconWidth + textWidth + borderSize * 3 + accelWidth + arrowWidth; + return QSize( width, h ); +} + +int PopupMenuEditor::itemHeight( const PopupMenuEditorItem * item ) const +{ + if ( !item || ( item && !item->isVisible() ) ) + return 0; + if ( item->isSeparator() ) + return 4; // FIXME: hardcoded ( get from styles )r + int padding = + borderSize * 6; + QAction * a = item->action(); + int h = a->iconSet().pixmap( QIconSet::Automatic, QIconSet::Normal ).rect().height(); + h = QMAX( h, fontMetrics().boundingRect( a->menuText() ).height() + padding ); + h = QMAX( h, fontMetrics().boundingRect( a->accel() ).height() + padding ); + return h; +} + +int PopupMenuEditor::itemPos( const PopupMenuEditorItem * item ) const +{ + PopupMenuEditor * that = ( PopupMenuEditor * ) this; + int y = 0; + PopupMenuEditorItem * i = that->itemList.first(); + while ( i ) { + if ( i == item ) + return y; + y += itemHeight( i ); + i = that->itemList.next(); + } + return y; +} + +int PopupMenuEditor::snapToItem( int y ) +{ + int iy = 0; + int dy = 0; + + PopupMenuEditorItem * i = itemList.first(); + + while ( i ) { + dy = itemHeight( i ); + if ( iy + dy / 2 > y ) + return iy; + iy += dy; + i = itemList.next(); + } + + return iy; +} + +void PopupMenuEditor::dropInPlace( PopupMenuEditorItem * i, int y ) +{ + int iy = 0; + int dy = 0; + int idx = 0; + + PopupMenuEditorItem * n = itemList.first(); + + while ( n ) { + dy = itemHeight( n ); + if ( iy + dy / 2 > y ) + break; + iy += dy; + idx++; + n = itemList.next(); + } + int same = itemList.findRef( i ); + AddActionToPopupCommand * cmd = new AddActionToPopupCommand( i18n( "Drop Item" ), formWnd, this, i, idx ); + formWnd->commandHistory()->addCommand( cmd ); + cmd->execute(); + currentIndex = ( same >= 0 && same < idx ) ? idx - 1 : idx; + currentField = 1; +} + +void PopupMenuEditor::dropInPlace( QActionGroup * g, int y ) +{ + if (!g->children()) + return; + QObjectList l = *g->children(); + for ( int i = 0; i < (int)l.count(); ++i ) { + QAction *a = ::qt_cast<QAction*>(l.at(i)); + QActionGroup *g = ::qt_cast<QActionGroup*>(l.at(i)); + if ( g ) + dropInPlace( g, y ); + else if ( a ) + dropInPlace( new PopupMenuEditorItem( a, this ), y ); + } +} + +void PopupMenuEditor::safeDec() +{ + do { + currentIndex--; + } while ( currentIndex > 0 && !currentItem()->isVisible() ); + if ( currentIndex == 0 && + !currentItem()->isVisible() && + parentMenu ) { + parentMenu->setFocus(); + } +} + +void PopupMenuEditor::safeInc() +{ + int max = itemList.count() + 1; + if ( currentIndex < max ) { + do { + currentIndex++; + } while ( currentIndex < max && !currentItem()->isVisible() ); // skip invisible items + } +} + +void PopupMenuEditor::clearCurrentField() +{ + if ( currentIndex >= (int)itemList.count() ) + return; // currentIndex is addItem or addSeparator + PopupMenuEditorItem * i = currentItem(); + hideSubMenu(); + if ( i->isSeparator() ) + return; + if ( currentField == 0 ) { + QIconSet icons( 0 ); + SetActionIconsCommand * cmd = new SetActionIconsCommand( i18n( "Remove Icon" ), + formWnd, + i->action(), + this, + icons ); + formWnd->commandHistory()->addCommand( cmd ); + cmd->execute(); + } else if ( currentField == 2 ) { + i->action()->setAccel( 0 ); + } + resizeToContents(); + showSubMenu(); + return; +} + +void PopupMenuEditor::navigateUp( bool ctrl ) +{ + if ( currentIndex > 0 ) { + hideSubMenu(); + if ( ctrl ) { + ExchangeActionInPopupCommand * cmd = + new ExchangeActionInPopupCommand( i18n( "Move Item Up" ), + formWnd, + this, + currentIndex, + currentIndex - 1 ); + formWnd->commandHistory()->addCommand( cmd ); + cmd->execute(); + safeDec(); + } else { + safeDec(); + } + showSubMenu(); + } else if ( parentMenu ) { + parentMenu->setFocus(); + parentMenu->update(); + } +} + +void PopupMenuEditor::navigateDown( bool ctrl ) +{ + hideSubMenu(); + if ( ctrl ) { + if ( currentIndex < ( (int)itemList.count() - 1 ) ) { // safe index + ExchangeActionInPopupCommand * cmd = + new ExchangeActionInPopupCommand( i18n( "Move Item Down" ), + formWnd, + this, + currentIndex, + currentIndex + 1 ); + formWnd->commandHistory()->addCommand( cmd ); + cmd->execute(); + safeInc(); + } + } else { // ! Ctrl + safeInc(); + } + if ( currentIndex >= (int)itemList.count() ) + currentField = 1; + showSubMenu(); +} + +void PopupMenuEditor::navigateLeft() +{ + if ( currentItem()->isSeparator() || + currentIndex >= (int)itemList.count() || + currentField == 0 ) { + if ( parentMenu ) { + hideSubMenu(); + parentMenu->setFocus(); + } else if ( !currentItem()->isSeparator() ) { + currentField = 2; + } + } else { + currentField--; + } +} + +void PopupMenuEditor::navigateRight() +{ + if ( !currentItem()->isSeparator() && + currentIndex < (int)itemList.count() ) { + if ( currentField == 2 ) { + focusOnSubMenu(); + } else { + currentField++; + currentField %= 3; + } + } +} + +void PopupMenuEditor::enterEditMode( QKeyEvent * e ) +{ + PopupMenuEditorItem * i = currentItem(); + + if ( i == &addSeparator ) { + i = createItem( new QSeparatorAction( 0 ) ); + } else if ( i->isSeparator() ) { + return; + } else if ( currentField == 0 ) { + choosePixmap(); + } else if ( currentField == 1 ) { + showLineEdit(); + return; + } else {// currentField == 2 + setAccelerator( e->key(), e->state() ); + } + showSubMenu(); + return; +} + +void PopupMenuEditor::leaveEditMode( QKeyEvent * e ) +{ + setFocus(); + lineEdit->hide(); + + PopupMenuEditorItem * i = 0; + if ( e && e->key() == Qt::Key_Escape ) { + update(); + return; + } + + if ( currentIndex >= (int)itemList.count() ) { + // new item was created + QAction * a = formWnd->mainWindow()->actioneditor()->newActionEx(); + QString actionText = lineEdit->text(); + actionText.replace("&&", "&"); + QString menuText = lineEdit->text(); + a->setText( actionText ); + a->setMenuText( menuText ); + i = createItem( a ); + QString n = constructName( i ); + formWindow()->unify( a, n, TRUE ); + a->setName( n ); + MetaDataBase::addEntry( a ); + MetaDataBase::setPropertyChanged( a, "menuText", TRUE ); + ActionEditor *ae = (ActionEditor*)formWindow()->mainWindow()->child( 0, "ActionEditor" ); + if ( ae ) + ae->updateActionName( a ); + } else { + i = itemList.at( currentIndex ); + RenameActionCommand * cmd = new RenameActionCommand( i18n( "Rename Item" ), + formWnd, + i->action(), + this, + lineEdit->text() ); + formWnd->commandHistory()->addCommand( cmd ); + cmd->execute(); + } + resizeToContents(); + + if ( !i ) + return; + + if ( i->isSeparator() ) + hideSubMenu(); + else + showSubMenu(); +} + +QString PopupMenuEditor::constructName( PopupMenuEditorItem *item ) +{ + QString s; + QString name = item->action()->menuText(); + QWidget *e = parentEditor(); + PopupMenuEditor *p = ::qt_cast<PopupMenuEditor*>(e); + if ( p ) { + int idx = p->find( item->m ); + PopupMenuEditorItem * i = ( idx > -1 ? p->at( idx ) : 0 ); + s = ( i ? QString( i->action()->name() ).remove( "Action" ) : QString( "" ) ); + } else { + MenuBarEditor *b = ::qt_cast<MenuBarEditor*>(e); + if ( b ) { + int idx = b->findItem( item->m ); + MenuBarEditorItem * i = ( idx > -1 ? b->item( idx ) : 0 ); + s = ( i ? i->menuText().lower() : QString( "" ) ); + } + } + // replace illegal characters + + return ( RenameMenuCommand::makeLegal( s ) + + RenameMenuCommand::makeLegal( name ) + "Action" ); +} |