/* ************************************************************************** description -------------------- copyright : (C) 2000-2003 by Andreas Zehender email : zehender@kde.org ************************************************************************** ************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * **************************************************************************/ #include <stdlib.h> #include <qlistview.h> #include <qheader.h> #include <qlayout.h> #include <qpopupmenu.h> #include <qcursor.h> #include <klocale.h> #include <kmessagebox.h> #include <kglobalsettings.h> #include <kiconloader.h> #include <kxmlguifactory.h> #include "pmtreeview.h" #include "pmtreeviewitem.h" #include "pmcommand.h" #include "pmpart.h" #include "pmscene.h" #include "pmobjectdrag.h" PMTreeViewWidget::PMTreeViewWidget( PMPart* part, QWidget* parent /*= 0*/, const char* name /*=0*/ ) : PMViewBase( parent, name ) { QHBoxLayout* hl = new QHBoxLayout( this ); PMTreeView* tv = new PMTreeView( part, this ); hl->addWidget( tv ); } QString PMTreeViewWidget::description( ) const { return i18n( "Object Tree" ); } PMTreeView::PMTreeView( PMPart* part, QWidget* parent /*= 0*/, const char* name /*= 0*/ ) : QListView( parent, name ) { addColumn( i18n( "Objects" ) ); header( )->hide( ); setRootIsDecorated( true ); setSorting( -1 ); setSelectionMode( Multi ); m_pPart = part; m_itemSelected = false; m_itemDeselected = false; m_selectionCleared = false; m_pLastSelected = 0; m_event = false; m_pressed = false; m_pDragOverItem = 0; m_acceptSelect = false; m_pressedItem = 0; viewport( )->setAcceptDrops( true ); viewport( )->setMouseTracking( true ); viewport( )->setFocusPolicy( QWidget::WheelFocus ); setFocusPolicy( QWidget::WheelFocus ); setAcceptDrops( true ); connect( part, SIGNAL( refresh( ) ), SLOT( slotRefresh( ) ) ); connect( part, SIGNAL( objectChanged( PMObject*, const int, QObject* ) ), SLOT( slotObjectChanged( PMObject*, const int, QObject* ) ) ); connect( part, SIGNAL( clear( ) ), SLOT( slotClear( ) ) ); connect( this, SIGNAL( objectChanged( PMObject*, const int, QObject* ) ), part, SLOT( slotObjectChanged( PMObject*, const int, QObject* ) ) ); slotRefresh( ); } PMTreeView::~PMTreeView( ) { emit destroyed( this ); } void PMTreeView::slotObjectChanged( PMObject* obj, const int mode, QObject* sender ) { PMTreeViewItem* pTreeItem = 0; bool as = m_acceptSelect; m_acceptSelect = true; if( sender != this ) { if( ( mode & PMCAdd ) && !( mode & PMCInsertError ) ) { // object was added if( !obj->parent( ) ) { // object has no parent, append it as top level item pTreeItem = new PMTreeViewItem( obj, this ); } else { // find the parent in the listview QListViewItem* pParentTreeItem = findObject( obj->parent( ) ); if( pParentTreeItem ) { PMObject* hObj = obj->prevSibling( ); QListViewItem* pSibling = 0; bool found = false; if( hObj ) { // find the previous sibling pSibling = pParentTreeItem->firstChild( ); while( pSibling && !found ) { if( ( ( PMTreeViewItem* ) pSibling )->object( ) == hObj ) found = true; else pSibling = pSibling->nextSibling( ); } } if( found ) { // object has sibling pTreeItem = new PMTreeViewItem( obj, pParentTreeItem, pSibling ); } else { // object has no sibling pTreeItem = new PMTreeViewItem( obj, pParentTreeItem ); } } } if( pTreeItem ) { // add child items if necessary if( obj->countChildren( ) > 0 ) addChildItems( pTreeItem ); } } if( mode & PMCDescription ) { if( !pTreeItem ) pTreeItem = findObject( obj ); if( pTreeItem ) pTreeItem->setDescriptions( ); } if( mode & PMCChildren ) { if( !pTreeItem ) pTreeItem = findObject( obj ); if( pTreeItem ) { // delete old items while( pTreeItem->firstChild( ) ) delete pTreeItem->firstChild( ); // create new addChildItems( pTreeItem ); pTreeItem->setOpen( true ); } } if( mode & PMCNewSelection ) { clearSelection( ); if( !pTreeItem ) pTreeItem = findObject( obj ); if( pTreeItem ) { PMTreeViewItem* p; for( p = pTreeItem->parent( ); p; p = p->parent( ) ) p->setOpen( true ); pTreeItem->setSelected( true ); setCurrentItem( pTreeItem ); } } if( mode & PMCDeselected ) { if( !pTreeItem ) pTreeItem = findObject( obj ); pTreeItem->setSelected( false ); } if( mode & PMCSelected ) { if( !pTreeItem ) pTreeItem = findObject( obj ); pTreeItem->setSelected( true ); } if( mode & PMCRemove ) { // object was removed, remove the listview item if( !pTreeItem ) pTreeItem = findObject( obj ); delete( pTreeItem ); } if( mode & PMCData ) { // special case for texture maps if( obj ) { if( obj->isA( "TextureMapBase" ) ) { if( !pTreeItem ) pTreeItem = findObject( obj ); if( pTreeItem ) { PMTreeViewItem* it = ( PMTreeViewItem* ) pTreeItem->firstChild( ); for( ; it; it = ( PMTreeViewItem* ) it->nextSibling( ) ) it->setDescriptions( ); } } } } } m_acceptSelect = as; } PMTreeViewItem* PMTreeView::findObject( const PMObject* obj ) { PMTreeViewItem* pTreeItem = 0; if( !obj->parent( ) ) { // top level object pTreeItem = ( PMTreeViewItem* ) firstChild( ); for( ; pTreeItem; pTreeItem = ( PMTreeViewItem* ) pTreeItem->nextSibling( ) ) if( pTreeItem->object( ) == obj ) return pTreeItem; } else { pTreeItem = findObject( obj->parent( ) ); if( pTreeItem ) { pTreeItem = ( PMTreeViewItem* ) pTreeItem->firstChild( ); for( ; pTreeItem; pTreeItem = ( PMTreeViewItem* ) pTreeItem->nextSibling( ) ) if( pTreeItem->object( ) == obj ) return pTreeItem; } } return 0; } void PMTreeView::selectItem( QListViewItem* /*sitem*/ ) { /* QListViewItem* pItem = 0; bool emitSig; emitSig = ( m_pSelectedObject != ( ( PMTreeViewItem* ) sitem )->object( ) ); m_pSelectedObject = ( ( PMTreeViewItem* ) sitem )->object( ); for( pItem = sitem->parent( ); pItem; pItem = pItem->parent( ) ) pItem->setOpen( true ); ensureItemVisible( sitem ); setCurrentItem( sitem ); setSelected( sitem, true ); if( emitSig ) emit objectSelected( m_pSelectedObject ); */ } void PMTreeView::addChildItems( PMTreeViewItem* item ) { PMObject* obj = 0; PMTreeViewItem* listItem = 0; for( obj = item->object( )->firstChild( ); obj; obj = obj->nextSibling( ) ) { // insert all child objects if( listItem ) listItem = new PMTreeViewItem( obj, item, listItem ); else // first child listItem = new PMTreeViewItem( obj, item ); // recursive call, if child has children if( obj->countChildren( ) > 0 ) addChildItems( listItem ); } } void PMTreeView::slotRefresh( ) { PMTreeViewItem* item; slotClear( ); // insert the top level items if( m_pPart->scene( ) ) { item = new PMTreeViewItem( m_pPart->scene( ), this ); addChildItems( item ); item->setOpen( true ); // item = new PMTreeViewItem( m_pPart->insertErrors( ), this ); // addChildItems( item ); // item->setOpen( true ); } } void PMTreeView::slotClear( ) { clear( ); m_pLastSelected = 0; m_pDragOverItem = 0; m_pressedItem = 0; } void PMTreeView::itemSelected( PMTreeViewItem* item, bool selected ) { repaintItem( item ); if( m_event ) { m_pLastSelected = item; if( selected ) m_itemSelected = true; else { if( m_itemDeselected ) m_selectionCleared = true; else m_itemDeselected = true; } } } void PMTreeView::contentsMousePressEvent( QMouseEvent * e ) { m_itemSelected = false; m_itemDeselected = false; m_pLastSelected = 0; m_selectionCleared = false; m_selectOnReleaseEvent = false; bool specialAction = false; QListViewItem* oldCurrent = currentItem( ); m_event = true; m_acceptSelect = true; QListView::contentsMousePressEvent( e ); m_event = false; m_acceptSelect = true; if( m_selectionCleared ) { emit objectChanged( 0, PMCNewSelection, this ); specialAction = true; } else if( m_itemSelected || m_itemDeselected ) { if( !( e->state( ) & ( ShiftButton | ControlButton ) ) ) { specialAction = true; // simple click, deselect all selected item // m_pLastSelected is the new selection if( m_itemSelected ) { clearSelection( ); m_pLastSelected->setSelected( true ); emit objectChanged( m_pLastSelected->object( ), PMCNewSelection, this ); } else { m_selectOnReleaseEvent = true; m_pLastSelected->setSelected( true ); } } else if( ( e->state( ) & ShiftButton ) && oldCurrent && m_pLastSelected ) { if( ( oldCurrent != m_pLastSelected ) && ( oldCurrent->parent( ) == m_pLastSelected->parent( ) ) ) { specialAction = true; // shift click, old current item has the same parent // as the new selection. Select all items between the two // items if( m_pLastSelected->object( )->isSelectable( ) ) { bool down = oldCurrent->itemPos( ) < m_pLastSelected->itemPos( ); QListViewItem* tmp; if( down ) { for( tmp = oldCurrent; tmp; tmp = tmp->nextSibling( ) ) { tmp->setSelected( true ); emit objectChanged( (( PMTreeViewItem* ) tmp)->object( ), PMCSelected, this ); if( tmp == m_pLastSelected ) break; } } else { for( tmp = m_pLastSelected; tmp; tmp = tmp->nextSibling( ) ) { tmp->setSelected( true ); emit objectChanged( (( PMTreeViewItem* ) tmp)->object( ), PMCSelected, this ); if( tmp == oldCurrent ) break; } } } else m_pLastSelected->setSelected( false ); } } } if( !specialAction ) { // no special action // object is selected or deselected, no other objects are changed if( m_itemSelected ) { if( m_pLastSelected->object( )->isSelectable( ) ) emit objectChanged( m_pLastSelected->object( ), PMCSelected, this ); else m_pLastSelected->setSelected( false ); } else if( m_itemDeselected ) emit objectChanged( m_pLastSelected->object( ), PMCDeselected, this ); } m_acceptSelect = false; } void PMTreeView::contentsMouseMoveEvent( QMouseEvent * e ) { m_itemSelected = false; m_itemDeselected = false; m_pLastSelected = 0; m_selectionCleared = false; m_event = true; QListView::contentsMouseMoveEvent( e ); m_event = false; // ignore all selections/deselections if( m_itemSelected || m_itemDeselected ) m_pLastSelected->setSelected( m_pLastSelected->object( )->isSelected( ) ); } void PMTreeView::viewportMousePressEvent( QMouseEvent* e ) { m_acceptSelect = true; QListView::viewportMousePressEvent( e ); m_acceptSelect = false; m_pressed = false; QPoint p = e->pos( ); if( e->button( ) & RightButton ) { if( m_pPart->factory( ) ) { QPopupMenu* m = ( QPopupMenu* ) m_pPart->factory( )->container( "treeViewPopup", m_pPart ); if( m ) m->exec( QCursor::pos( ) ); } return; } PMTreeViewItem *item = ( PMTreeViewItem* )itemAt( p ); if( item ) { // check if the root decoration was clicked if( !( p.x( ) > header( )->cellPos( header( )->mapToActual( 0 ) ) + treeStepSize( ) * ( item->depth( ) + ( rootIsDecorated( ) ? 1 : 0 ) ) + itemMargin( ) || p.x( ) < header( )->cellPos( header( )->mapToActual( 0 ) ) ) ) item = 0; // p is on the root decoration } if( item ) { if( e->button( ) == LeftButton || e->button( ) == MidButton ) { m_pressed = true; m_pressedPos = e->pos( ); m_pressedItem = item; return; } } } void PMTreeView::viewportMouseReleaseEvent( QMouseEvent* e ) { QListView::viewportMouseReleaseEvent( e ); if( !m_pressed ) return; m_pressed = false; m_pressedItem = 0L; if( m_selectOnReleaseEvent ) { if( m_pLastSelected ) { m_acceptSelect = true; clearSelection( ); m_pLastSelected->setSelected( true ); m_acceptSelect = false; emit objectChanged( m_pLastSelected->object( ), PMCNewSelection, this ); } } } void PMTreeView::viewportMouseMoveEvent( QMouseEvent *e ) { QListView::viewportMouseMoveEvent( e ); if( m_pressed && m_pressedItem ) { int x = e->pos( ).x( ); int y = e->pos( ).y( ); //Is it time to start a drag? if( abs( x - m_pressedPos.x( ) ) > KGlobalSettings::dndEventDelay( ) || abs( y - m_pressedPos.y( ) ) > KGlobalSettings::dndEventDelay( ) ) { m_selectOnReleaseEvent = false; // Calculate hotspot QPoint hotspot; PMObjectList sortedList = m_pPart->selectedObjects( ); // Do not handle more mouse move or mouse release events m_pressed = false; if( sortedList.count( ) > 0 ) { PMObjectDrag* d = new PMObjectDrag( m_pPart, sortedList, viewport( ) ); hotspot.setX( m_pressedItem->pixmap( 0 )->width( ) / 2 ); hotspot.setY( m_pressedItem->pixmap( 0 )->height( ) / 2 ); if( sortedList.count( ) == 1 ) d->setPixmap( SmallIcon( sortedList.first( )->pixmap( ) ), hotspot ); else d->setPixmap( SmallIcon( "pmdrag" ) ); if( d->drag( ) ) { kdDebug( PMArea ) << "Drag returned true\n"; if( !targetDisplaysPart( d->target( ) ) ) m_pPart->dragMoveSelectionTo( 0 ); } } } } } void PMTreeView::viewportDragMoveEvent( QDragMoveEvent *e ) { bool accept = false; if( m_pPart->isReadWrite( ) ) { if( PMObjectDrag::canDecode( e, m_pPart ) ) { PMTreeViewItem *item = ( PMTreeViewItem* ) itemAt( e->pos( ) ); PMObject* obj = 0; if( !item ) { accept = false; /* if( e->source( ) == viewport( ) ) { if( m_pPart->scene( )->isSelected( ) ) accept = false; else accept = true; } else accept = true; obj = m_pPart->scene( ); */ m_pDragOverItem = 0L; obj = 0; } else { obj = item->object( ); if( ( obj->isSelectable( ) && !obj->isSelected( ) ) || ( e->source( ) != viewport( ) ) ) { accept = true; setCurrentItem( item ); m_pDragOverItem = item; } else { accept = false; m_pDragOverItem = 0L; } } if( accept ) { accept = false; if( !obj->isReadOnly( ) ) accept = true; if( obj->parent( ) ) if( !obj->parent( )->isReadOnly( ) ) accept = true; } } else accept = false; } else accept = false; if( accept ) e->acceptAction( ); else e->ignore( ); } void PMTreeView::viewportDragEnterEvent( QDragEnterEvent *e ) { m_pDragOverItem = 0L; if( m_pPart->isReadWrite( ) ) e->accept( PMObjectDrag::canDecode( e, m_pPart ) ); else e->ignore( ); } void PMTreeView::viewportDragLeaveEvent( QDragLeaveEvent* ) { m_pDragOverItem = 0L; } void PMTreeView::viewportDropEvent( QDropEvent* e ) { PMObject* obj; if( m_pPart->isReadWrite( ) ) { if( m_pDragOverItem ) obj = m_pDragOverItem->object( ); else obj = m_pPart->scene( ); if( PMObjectDrag::canDecode( e, m_pPart ) ) { if( targetDisplaysPart( e->source( ) ) && ( e->action( ) == QDropEvent::Move ) ) { if( m_pPart->dragMoveSelectionTo( obj ) ) e->acceptAction( ); else e->ignore( ); } else { if( m_pPart->drop( obj, e ) ) e->acceptAction( ); else e->ignore( ); } } else e->ignore( ); } else e->ignore( ); m_pDragOverItem = 0L; } void PMTreeView::focusOutEvent( QFocusEvent* e ) { QWidget::focusOutEvent( e ); m_pressed = false; m_pressedItem = 0; } void PMTreeView::focusInEvent( QFocusEvent* e ) { QWidget::focusInEvent( e ); m_pressed = false; m_pressedItem = 0; } void PMTreeView::keyPressEvent( QKeyEvent* e ) { QListViewItem* current = currentItem( ); QListViewItem* newSelection = 0; bool accept = false; bool deleteItem = false; bool pasteItem = false; if( current ) { switch( e->key( ) ) { case Qt::Key_Up: newSelection = current->itemAbove( ); accept = true; break; case Qt::Key_Down: newSelection = current->itemBelow( ); accept = true; break; case Qt::Key_Left: newSelection = current->parent( ); accept = true; break; case Qt::Key_Right: newSelection = current->firstChild( ); accept = true; break; case Qt::Key_Plus: current->setOpen( true ); accept = true; break; case Qt::Key_Minus: current->setOpen( false ); accept = true; case Qt::Key_Delete: deleteItem = true; accept = true; break; case Qt::CTRL+Qt::Key_V: case Qt::SHIFT+Qt::Key_Insert: pasteItem = true; accept = true; break; } } if( newSelection ) { m_acceptSelect = true; clearSelection( ); newSelection->setSelected( true ); setCurrentItem( newSelection ); ensureItemVisible( newSelection ); m_acceptSelect = false; emit objectChanged( ( ( PMTreeViewItem* ) newSelection )->object( ), PMCNewSelection, this ); } if( deleteItem && m_pPart->isReadWrite( ) ) { m_pPart->slotEditDelete( ); m_pPart->setModified( true ); } if( pasteItem && m_pPart->isReadWrite( ) ) { m_pPart->slotEditPaste( ); m_pPart->setModified( true ); } if( accept ) e->accept( ); else e->ignore( ); QWidget::keyPressEvent( e ); } bool PMTreeView::targetDisplaysPart( QWidget* target ) { bool result = false; if( !target ) // another application result = false; else if( target == viewport( ) ) // self result = true; else { // Widget may be a view port // find the tree view QWidget* t = target; while( t && !t->isA( "PMTreeView" ) ) t = t->parentWidget( ); if( t ) if( ( ( PMTreeView* ) t )->part( ) == m_pPart ) result = true; } return result; } QString PMTreeViewFactory::description( ) const { return i18n( "Object Tree" ); } #include "pmtreeview.moc"