diff options
Diffstat (limited to 'chalk/ui/layerlist.cpp')
-rw-r--r-- | chalk/ui/layerlist.cpp | 1325 |
1 files changed, 1325 insertions, 0 deletions
diff --git a/chalk/ui/layerlist.cpp b/chalk/ui/layerlist.cpp new file mode 100644 index 00000000..976ec84d --- /dev/null +++ b/chalk/ui/layerlist.cpp @@ -0,0 +1,1325 @@ +/* + Copyright (c) 2005 Gábor Lehel <illissius@gmail.com> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + + +#include "layerlist.h" + +#include <tqtooltip.h> +#include <tqbitmap.h> +#include <tqcursor.h> +#include <tqimage.h> +#include <tqheader.h> +#include <tqpainter.h> +#include <tqpixmap.h> +#include <tqsimplerichtext.h> +#include <tqtimer.h> + +#include <kapplication.h> +#include <kdebug.h> +#include <kglobal.h> +#include <kglobalsettings.h> +#include <kiconloader.h> +#include <klineedit.h> +#include <klocale.h> +#include <kpopupmenu.h> +#include <kstringhandler.h> + +class LayerItemIterator: public TQListViewItemIterator +{ +public: + LayerItemIterator( LayerList *list ): TQListViewItemIterator( list ) { } + LayerItemIterator( LayerList *list, IteratorFlag flags ): TQListViewItemIterator( list, flags ) { } + LayerItemIterator( LayerItem *item ): TQListViewItemIterator( item ) { } + LayerItemIterator( LayerItem *item, IteratorFlag flags ): TQListViewItemIterator( item, flags ) { } + LayerItem *operator*() { return static_cast<LayerItem*>( TQListViewItemIterator::operator*() ); } +}; + +struct LayerProperty +{ + TQString name; + TQString displayName; + TQPixmap enabledIcon; + TQPixmap disabledIcon; + bool defaultValue; + bool validForFolders; + + LayerProperty(): defaultValue( false ), validForFolders( true ) { } + LayerProperty( const TQString &pname, const TQString &pdisplayName, const TQPixmap &enabled, const TQPixmap &disabled, + bool pdefaultValue, bool pvalidForFolders ) + : name( pname ), + displayName( pdisplayName ), + enabledIcon( enabled ), + disabledIcon( disabled ), + defaultValue( pdefaultValue ), + validForFolders( pvalidForFolders ) + { } +}; + +class LayerToolTip; +class LayerList::Private +{ +public: + LayerItem *activeLayer; + bool foldersCanBeActive; + bool previewsShown; + int itemHeight; + TQValueList<LayerProperty> properties; + KPopupMenu contextMenu; + LayerToolTip *tooltip; + + Private( TQWidget *tqparent, LayerList *list ); + ~Private(); +}; + +class LayerItem::Private +{ +public: + bool isFolder; + int id; + TQValueList<bool> properties; + TQImage *previewImage; + bool previewChanged; + TQPixmap scaledPreview; + TQSize previewSize; + TQPoint previewOffset; + + Private( int pid ): isFolder( false ), id( pid ), previewImage( 0 ), previewChanged( false ) + { } +}; + +static const int MAX_SIZE = 256; +class LayerToolTip: public TQToolTip, public TQFrame +{ + LayerList *m_list; + LayerItem *m_item; + TQPoint m_pos; + TQTimer m_timer; + TQImage m_img; + +public: + LayerToolTip( TQWidget *tqparent, LayerList *list ) + : TQToolTip( tqparent ), + TQFrame( 0, 0, WStyle_Customize | WStyle_NoBorder | WStyle_Tool | WStyle_StaysOnTop | WX11BypassWM | WNoAutoErase ), + m_list( list ) + { + TQFrame::setPalette( TQToolTip::palette() ); + connect( &m_timer, TQT_SIGNAL( timeout() ), m_list, TQT_SLOT( hideTip() ) ); + tqApp->installEventFilter( this ); + } + + virtual void maybeTip( const TQPoint &pos ) + { + m_pos = pos; + LayerItem *prev = m_item; + m_item = static_cast<LayerItem*>(m_list->itemAt( m_pos )); + if( TQToolTip::tqparentWidget() && m_list->showToolTips() && m_item ) + { + if( m_item != prev ) + hideTip(); + showTip(); + } + else + hideTip(); + } + + void showTip() + { + m_img = m_item->tooltipPreview(); + m_timer.start( 15000, true ); + if( !isVisible() || tqsizeHint() != size() ) + { + resize( tqsizeHint() ); + position(); + } + if( !isVisible() ) + show(); + else + update(); + } + + void hideTip() + { + if( !isVisible() ) + return; + TQFrame::hide(); + TQToolTip::hide(); + m_timer.stop(); + m_img.reset(); + m_list->triggerUpdate(); + } + + virtual void drawContents( TQPainter *painter ) + { + TQPixmap buf( width(), height() ); + TQPainter p( &buf ); + buf.fill( tqcolorGroup().background() ); + p.setPen( tqcolorGroup().foreground() ); + p.drawRect( buf.rect() ); + + TQSimpleRichText text( m_item->tooltip(), TQToolTip::font() ); + text.setWidth( TQCOORD_MAX ); + + p.translate( 5, 5 ); + if( !m_img.isNull() ) + { + if( m_img.width() > MAX_SIZE || m_img.height() > MAX_SIZE ) + m_img = m_img.scale( MAX_SIZE, MAX_SIZE, TQ_ScaleMin ); + int y = 0; + if( m_img.height() < text.height() ) + y = text.height()/2 - m_img.height()/2; + p.drawImage( 0, y, m_img ); + p.drawRect( -1, y-1, m_img.width()+2, m_img.height()+2 ); + p.translate( m_img.width() + 10, 0 ); + } + + text.draw( &p, 0, 0, rect(), tqcolorGroup() ); + + painter->drawPixmap( 0, 0, buf ); + } + + virtual TQSize tqsizeHint() const + { + if( !m_item ) + return TQSize( 0, 0 ); + + TQSimpleRichText text( m_item->tooltip(), TQToolTip::font() ); + text.setWidth( TQCOORD_MAX ); + + int width = text.widthUsed(); + if( !m_img.isNull() ) + width += kMin( m_img.width(), MAX_SIZE ) + 10; + width += 10; + + int height = text.height(); + if( !m_img.isNull() && kMin( m_img.height(), MAX_SIZE ) > height ) + height = kMin( m_img.height(), MAX_SIZE ); + height += 10; + + return TQSize( width, height ); + } + + void position() + { + const TQRect drect = TQApplication::desktop()->availableGeometry( TQToolTip::tqparentWidget() ); + const TQSize size = tqsizeHint(); + const int width = size.width(), height = size.height(); + const TQRect tmp = m_item->rect(); + const TQRect irect( m_list->viewport()->mapToGlobal( m_list->contentsToViewport(tmp.topLeft()) ), tmp.size() ); + + int y; + if( irect.bottom() + height < drect.bottom() ) + y = irect.bottom(); + else + y = kMax( drect.top(), irect.top() - height ); + + int x = kMax( drect.x(), TQToolTip::tqparentWidget()->mapToGlobal( m_pos ).x() - width/2 ); + if( x + width > drect.right() ) + x = drect.right() - width; + + move( x, y ); + } + + virtual bool eventFilter( TQObject *, TQEvent *e ) + { + if( isVisible() ) + switch ( e->type() ) + { + case TQEvent::KeyPress: + case TQEvent::KeyRelease: + case TQEvent::MouseButtonPress: + case TQEvent::MouseButtonRelease: + //case TQEvent::MouseMove: + case TQEvent::FocusIn: + case TQEvent::FocusOut: + case TQEvent::Wheel: + case TQEvent::Leave: + hideTip(); + default: break; + } + + return false; + } +}; + +LayerList::Private::Private( TQWidget *tqparent, LayerList *list ) + : activeLayer( 0 ), foldersCanBeActive( false ), previewsShown( false ), itemHeight( 32 ), + tooltip( new LayerToolTip( tqparent, list ) ) { } + +LayerList::Private::~Private() +{ + delete tooltip; + tooltip = 0; +} + +static int getID() +{ + static int id = -2; + return id--; +} + +static TQSize iconSize() { return TQIconSet::iconSize( TQIconSet::Small ); } + + +/////////////// +// LayerList // +/////////////// + +LayerList::LayerList( TQWidget *tqparent, const char *name ) + : super( tqparent, name ), d( new Private( viewport(), this ) ) +{ + setSelectionMode( TQListView::Extended ); + setRootIsDecorated( true ); + setSorting( -1 ); + setSortColumn( -1 ); + setAllColumnsShowFocus( true ); + setFullWidth( true ); + setItemsRenameable( false ); + setDropHighlighter( true ); + setDefaultRenameAction( TQListView::Accept ); + setDragEnabled( true ); + setAcceptDrops( true ); + setItemsMovable( true ); + addColumn( TQString() ); + header()->hide(); + + TQToolTip::add(this, i18n("Right-click to create folders. Click on the layername to change the layer's name. Click and drag to move layers.")); + + setNumRows( 2 ); + + connect( this, TQT_SIGNAL( itemRenamed( TQListViewItem*, const TQString&, int ) ), + TQT_SLOT( slotItemRenamed( TQListViewItem*, const TQString&, int ) ) ); + connect( this, TQT_SIGNAL( moved( TQPtrList<TQListViewItem>&, TQPtrList<TQListViewItem>&, TQPtrList<TQListViewItem>& ) ), + TQT_SLOT( slotItemMoved( TQPtrList<TQListViewItem>&, TQPtrList<TQListViewItem>&, TQPtrList<TQListViewItem>& ) ) ); + connect( this, TQT_SIGNAL( onItem( TQListViewItem* ) ), TQT_SLOT( hideTip() ) ); + connect( this, TQT_SIGNAL( onViewport() ), TQT_SLOT( hideTip() ) ); +} + +LayerList::~LayerList() +{ + delete d; +} + +void LayerList::addProperty( const TQString &name, const TQString &displayName, const TQIconSet &icon, + bool defaultValue, bool validForFolders ) +{ + addProperty( name, displayName, icon.pixmap( TQIconSet::Small, TQIconSet::Normal ), icon.pixmap( TQIconSet::Small, TQIconSet::Disabled ), defaultValue, validForFolders ); +} + +void LayerList::addProperty( const TQString &name, const TQString &displayName, TQPixmap enabled, TQPixmap disabled, + bool defaultValue, bool validForFolders ) +{ + d->properties.append( LayerProperty( name, displayName, enabled, disabled, defaultValue, validForFolders ) ); + + for( LayerItemIterator it( this ); *it; ++it ) + (*it)->d->properties.append( defaultValue ); + + //we do this only afterwards in case someone wants to access the other items in a connected slot... + for( LayerItemIterator it( this ); *it; ++it ) + if( validForFolders || !(*it)->isFolder() ) + { + emit propertyChanged( *it, name, defaultValue ); + emit propertyChanged( (*it)->id(), name, defaultValue ); + } + + triggerUpdate(); +} + +LayerItem *LayerList::layer( int id ) const +{ + if( !firstChild() || id == -1 ) + return 0; + + for( LayerItemIterator it( firstChild() ); *it; ++it ) + if( (*it)->id() == id ) + return (*it); + + return 0; +} + +LayerItem *LayerList::folder( int id ) const +{ + if( !firstChild() || id == -1 ) + return 0; + + for( LayerItemIterator it( firstChild() ); *it; ++it ) + if( (*it)->id() == id && (*it)->isFolder() ) + return (*it); + + return 0; +} + +LayerItem *LayerList::activeLayer() const +{ + return d->activeLayer; +} + +int LayerList::activeLayerID() const +{ + if( activeLayer() ) + return activeLayer()->id(); + return -1; +} + +TQValueList<LayerItem*> LayerList::selectedLayers() const +{ + if( !firstChild() ) + return TQValueList<LayerItem*>(); + + TQValueList<LayerItem*> layers; + for( LayerItemIterator it( firstChild() ); *it; ++it ) + if( (*it)->isSelected() ) + layers.append( *it ); + + return layers; +} + +TQValueList<int> LayerList::selectedLayerIDs() const +{ + const TQValueList<LayerItem*> layers = selectedLayers(); + TQValueList<int> ids; + for( int i = 0, n = layers.count(); i < n; ++i ) + ids.append( layers[i]->id() ); + + return ids; +} + +bool LayerList::foldersCanBeActive() const +{ + return d->foldersCanBeActive; +} + +bool LayerList::previewsShown() const +{ + return d->previewsShown; +} + +int LayerList::itemHeight() const +{ + return d->itemHeight; +} + +int LayerList::numRows() const +{ + if( itemHeight() < kMax( fontMetrics().height(), iconSize().height() ) ) + return 0; + + return ( itemHeight() - fontMetrics().height() ) / iconSize().height() + 1; +} + +void LayerList::makeFolder( int id ) +{ + LayerItem* const l = layer( id ); + if( l ) + l->makeFolder(); +} + +bool LayerList::isFolder( int id ) const +{ + LayerItem* const l = layer( id ); + if( !l ) + return false; + + return l->isFolder(); +} + +TQString LayerList::displayName( int id ) const +{ + LayerItem* const l = layer( id ); + if( !l ) + return TQString(); //should be more severe... + + return l->displayName(); +} + +bool LayerList::property( int id, const TQString &name ) const +{ + LayerItem* const l = layer( id ); + if( !l ) + return false; //should be more severe... + + return l->property( name ); +} + +KPopupMenu *LayerList::contextMenu() const +{ + return &( d->contextMenu ); +} + +void LayerList::setFoldersCanBeActive( bool can ) //SLOT +{ + d->foldersCanBeActive = can; + if( !can && activeLayer() && activeLayer()->isFolder() ) + { + d->activeLayer = 0; + emit activated( static_cast<LayerItem*>( 0 ) ); + emit activated( -1 ); + } +} + +void LayerList::setPreviewsShown( bool show ) //SLOT +{ + d->previewsShown = show; + triggerUpdate(); +} + +void LayerList::setItemHeight( int height ) //SLOT +{ + d->itemHeight = height; + for( LayerItemIterator it( this ); *it; ++it ) + (*it)->setup(); + triggerUpdate(); +} + +void LayerList::setNumRows( int rows ) +{ + if( rows < 1 ) + return; + + if( rows == 1 ) + setItemHeight( kMax( fontMetrics().height(), iconSize().height() ) ); + else + setItemHeight( fontMetrics().height() + ( rows - 1 ) * iconSize().height() ); +} + +void LayerList::setActiveLayer( LayerItem *layer ) //SLOT +{ + if( !foldersCanBeActive() && layer && layer->isFolder() ) + return; + + ensureItemVisible( layer ); + + if( d->activeLayer == layer ) + return; + + d->activeLayer = layer; + + if( currentItem() != layer ) + setCurrentItem( layer ); + else + { + int n = 0; + for( LayerItemIterator it( this, LayerItemIterator::Selected ); n < 2 && (*it); ++it ) { n++; } + if( n == 1 ) + (*LayerItemIterator( this, LayerItemIterator::Selected ))->setSelected( false ); + if( layer ) + layer->setSelected( true ); + } + + emit activated( layer ); + if( layer ) + emit activated( layer->id() ); + else + emit activated( -1 ); +} + +void LayerList::setActiveLayer( int id ) //SLOT +{ + setActiveLayer( layer( id ) ); +} + +void LayerList::setLayerDisplayName( LayerItem *layer, const TQString &displayName ) +{ + if( !layer ) + return; + + layer->setDisplayName( displayName ); +} + +void LayerList::setLayerDisplayName( int id, const TQString &displayName ) +{ + setLayerDisplayName( layer( id ), displayName ); +} + +void LayerList::setLayerProperty( LayerItem *layer, const TQString &name, bool on ) //SLOT +{ + if( !layer ) + return; + + layer->setProperty( name, on ); +} + +void LayerList::setLayerProperty( int id, const TQString &name, bool on ) //SLOT +{ + setLayerProperty( layer( id ), name, on ); +} + +void LayerList::toggleLayerProperty( LayerItem *layer, const TQString &name ) //SLOT +{ + if( !layer ) + return; + + layer->toggleProperty( name ); +} + +void LayerList::toggleLayerProperty( int id, const TQString &name ) //SLOT +{ + toggleLayerProperty( layer( id ), name ); +} + +void LayerList::setLayerPreviewImage( LayerItem *layer, TQImage *image ) +{ + if( !layer ) + return; + + layer->setPreviewImage( image ); +} + +void LayerList::setLayerPreviewImage( int id, TQImage *image ) +{ + setLayerPreviewImage( layer( id ), image ); +} + +void LayerList::layerPreviewChanged( LayerItem *layer ) +{ + if( !layer ) + return; + + layer->previewChanged(); +} + +void LayerList::layerPreviewChanged( int id ) +{ + layerPreviewChanged( layer( id ) ); +} + +LayerItem *LayerList::addLayer( const TQString &displayName, LayerItem *after, int id ) //SLOT +{ + return new LayerItem( displayName, this, after, id ); +} + +LayerItem *LayerList::addLayer( const TQString &displayName, int afterID, int id ) //SLOT +{ + return new LayerItem( displayName, this, layer( afterID ), id ); +} + +//SLOT +LayerItem *LayerList::addLayerToParent( const TQString &displayName, LayerItem *tqparent, LayerItem *after, int id ) +{ + if( tqparent && tqparent->isFolder() ) + return tqparent->addLayer( displayName, after, id ); + else + return 0; +} + +LayerItem *LayerList::addLayerToParent( const TQString &displayName, int tqparentID, int afterID, int id ) //SLOT +{ + return addLayerToParent( displayName, folder( tqparentID ), layer( afterID ), id ); +} + +void LayerList::moveLayer( LayerItem *layer, LayerItem *tqparent, LayerItem *after ) //SLOT +{ + if( !layer ) + return; + + if( tqparent && !tqparent->isFolder() ) + tqparent = 0; + + if( layer->tqparent() == tqparent && layer->prevSibling() == after ) + return; + + TQListViewItem *current = currentItem(); + + moveItem( layer, tqparent, after ); + + emit layerMoved( layer, tqparent, after ); + emit layerMoved( layer->id(), tqparent ? tqparent->id() : -1, after ? after->id() : -1 ); + + setCurrentItem( current ); //HACK, sometimes TQt changes this under us +} + +void LayerList::moveLayer( int id, int tqparentID, int afterID ) //SLOT +{ + moveLayer( layer( id ), folder( tqparentID ), layer( afterID ) ); +} + +void LayerList::removeLayer( LayerItem *layer ) //SLOT +{ + delete layer; +} + +void LayerList::removeLayer( int id ) //SLOT +{ + delete layer( id ); +} + +void LayerList::contentsMousePressEvent( TQMouseEvent *e ) +{ + LayerItem *item = static_cast<LayerItem*>( itemAt( contentsToViewport( e->pos() ) ) ); + + if( item ) + { + TQMouseEvent m( TQEvent::MouseButtonPress, item->mapFromListView( e->pos() ), e->button(), e->state() ); + if( !item->mousePressEvent( &m ) ) + super::contentsMousePressEvent( e ); + } + else + { + super::contentsMousePressEvent( e ); + if( e->button() == Qt::RightButton ) + showContextMenu(); + } +} + +void LayerList::contentsMouseDoubleClickEvent( TQMouseEvent *e ) +{ + super::contentsMouseDoubleClickEvent( e ); + if( LayerItem *layer = static_cast<LayerItem*>( itemAt( contentsToViewport( e->pos() ) ) ) ) + { + if( !layer->iconsRect().tqcontains( layer->mapFromListView( e->pos() ) ) ) + { + emit requestLayerProperties( layer ); + emit requestLayerProperties( layer->id() ); + } + } + else + { + emit requestNewLayer( static_cast<LayerItem*>( 0 ), static_cast<LayerItem*>( 0 ) ); + emit requestNewLayer( -1, -1 ); + } +} + +void LayerList::findDrop( const TQPoint &pos, TQListViewItem *&tqparent, TQListViewItem *&after ) +{ + LayerItem *item = static_cast<LayerItem*>( itemAt( contentsToViewport( pos ) ) ); + if( item && item->isFolder() ) + { + tqparent = item; + after = 0; + } + else + super::findDrop( pos, tqparent, after ); +} + +void LayerList::showContextMenu() +{ + LayerItem *layer = static_cast<LayerItem*>( itemAt( viewport()->mapFromGlobal( TQCursor::pos() ) ) ); + if( layer ) + setCurrentItem( layer ); + d->contextMenu.clear(); + constructMenu( layer ); + menuActivated( d->contextMenu.exec( TQCursor::pos() ), layer ); +} + +void LayerList::hideTip() +{ + d->tooltip->hideTip(); +} + +void LayerList::maybeTip() +{ + d->tooltip->maybeTip( d->tooltip->TQToolTip::tqparentWidget()->mapFromGlobal( TQCursor::pos() ) ); +} + +void LayerList::constructMenu( LayerItem *layer ) +{ + if( layer ) + { + for( int i = 0, n = d->properties.count(); i < n; ++i ) + if( !layer->isFolder() || d->properties[i].validForFolders ) + d->contextMenu.insertItem( layer->d->properties[i] ? d->properties[i].enabledIcon : d->properties[i].disabledIcon, d->properties[i].displayName, MenuItems::COUNT + i ); + d->contextMenu.insertItem( SmallIconSet( "info" ), i18n( "&Properties" ), MenuItems::LayerProperties ); + d->contextMenu.insertSeparator(); + d->contextMenu.insertItem( SmallIconSet( "editdelete" ), + selectedLayers().count() > 1 ? i18n( "Remove Layers" ) + : layer->isFolder() ? i18n( "&Remove Folder" ) + : i18n( "&Remove Layer" ), MenuItems::RemoveLayer ); + } + d->contextMenu.insertItem( SmallIconSet( "filenew" ), i18n( "&New Layer" ), MenuItems::NewLayer ); + d->contextMenu.insertItem( SmallIconSet( "folder" ), i18n( "New &Folder" ), MenuItems::NewFolder ); +} + +void LayerList::menuActivated( int id, LayerItem *layer ) +{ + const TQValueList<LayerItem*> selected = selectedLayers(); + + LayerItem *tqparent = ( layer && layer->isFolder() ) ? layer : 0; + LayerItem *after = 0; + if( layer && !tqparent ) + { + tqparent = layer->tqparent(); + after = layer->prevSibling(); + } + switch( id ) + { + case MenuItems::NewLayer: + emit requestNewLayer( tqparent, after ); + emit requestNewLayer( tqparent ? tqparent->id() : -1, after ? after->id() : -1 ); + break; + case MenuItems::NewFolder: + emit requestNewFolder( tqparent, after ); + emit requestNewFolder( tqparent ? tqparent->id() : -1, after ? after->id() : -1 ); + break; + case MenuItems::RemoveLayer: + { + TQValueList<int> ids; + for( int i = 0, n = selected.count(); i < n; ++i ) + { + ids.append( selected[i]->id() ); + emit requestRemoveLayer( selected[i]->id() ); + } + emit requestRemoveLayers( ids ); + } + for( int i = 0, n = selected.count(); i < n; ++i ) + emit requestRemoveLayer( selected[i] ); + emit requestRemoveLayers( selected ); + break; + case MenuItems::LayerProperties: + if( layer ) + { + emit requestLayerProperties( layer ); + emit requestLayerProperties( layer->id() ); + } + break; + default: + if( id >= MenuItems::COUNT && layer ) + for( int i = 0, n = selected.count(); i < n; ++i ) + selected[i]->toggleProperty( d->properties[ id - MenuItems::COUNT ].name ); + } +} + +void LayerList::slotItemRenamed( TQListViewItem *item, const TQString &text, int col ) +{ + if( !item || col != 0 ) + return; + + emit displayNameChanged( static_cast<LayerItem*>( item ), text ); + emit displayNameChanged( static_cast<LayerItem*>( item )->id(), text ); +} + +void LayerList::slotItemMoved( TQPtrList<TQListViewItem> &items, TQPtrList<TQListViewItem> &/*afterBefore*/, TQPtrList<TQListViewItem> &afterNow ) +{ + for( int i = 0, n = items.count(); i < n; ++i ) + { + LayerItem *l = static_cast<LayerItem*>( items.at(i) ), *a = static_cast<LayerItem*>( afterNow.at(i) ); + if( !l ) + continue; + + if( l->tqparent() ) + l->tqparent()->setOpen( true ); + + emit layerMoved( l, l->tqparent(), a ); + emit layerMoved( l->id(), l->tqparent() ? l->tqparent()->id() : -1, a ? a->id() : -1 ); + } +} + +void LayerList::setCurrentItem( TQListViewItem *item ) +{ + if( !item ) + return; + + super::setCurrentItem( item ); + ensureItemVisible( item ); + int n = 0; + for( LayerItemIterator it( this, LayerItemIterator::Selected ); n < 2 && (*it); ++it ) { n++; } + if( n == 1 ) + (*LayerItemIterator( this, LayerItemIterator::Selected ))->setSelected( false ); + item->setSelected( true ); + if( activeLayer() != item ) + setActiveLayer( static_cast<LayerItem*>(item) ); +} + + +/////////////// +// LayerItem // +/////////////// + +LayerItem::LayerItem( const TQString &displayName, LayerList *p, LayerItem *after, int id ) + : super( p, after ), d( new Private( id ) ) +{ + init(); + setDisplayName( displayName ); +} + +LayerItem::LayerItem( const TQString &displayName, LayerItem *p, LayerItem *after, int id ) + : super( ( p && p->isFolder() ) ? p : 0, after ), d( new Private( id ) ) +{ + init(); + setDisplayName( displayName ); +} + +void LayerItem::init() +{ + if( d->id < 0 ) + d->id = getID(); + + for( int i = 0, n = listView()->d->properties.count(); i < n; ++i ) + d->properties.append( listView()->d->properties[i].defaultValue ); + + if( tqparent()) + tqparent()->setOpen( true ); +} + +LayerItem::~LayerItem() +{ + if (listView() && (listView()->activeLayer() == this || tqcontains(listView()->activeLayer()))) + listView()->setActiveLayer( static_cast<LayerItem*>( 0 ) ); + delete d; +} + +void LayerItem::makeFolder() +{ + d->isFolder = true; + setPixmap( 0, SmallIcon( "folder", 16 ) ); + if( isActive() && !listView()->foldersCanBeActive() ) + listView()->setActiveLayer( static_cast<LayerItem*>( 0 ) ); +} + +bool LayerItem::isFolder() const +{ + return d->isFolder; +} + +bool LayerItem::tqcontains(const LayerItem *item) +{ + TQListViewItemIterator it(this); + + while (it.current()) { + if (static_cast<const LayerItem *>(it.current()) == item) { + return true; + } + ++it; + } + return false; +} + +int LayerItem::id() const +{ + return d->id; +} + +TQString LayerItem::displayName() const +{ + return text( 0 ); +} + +void LayerItem::setDisplayName( const TQString &s ) +{ + if( displayName() == s ) + return; + setText( 0, s ); + emit listView()->displayNameChanged( this, s ); + emit listView()->displayNameChanged( id(), s ); +} + +bool LayerItem::isActive() const +{ + return listView()->activeLayer() == this; +} + +void LayerItem::setActive() +{ + listView()->setActiveLayer( this ); +} + +bool LayerItem::property( const TQString &name ) const +{ + int i = listView()->d->properties.count() - 1; + while( i && listView()->d->properties[i].name != name ) + --i; + + if( i < 0 ) + return false; //should do something more severe... but what? + + return d->properties[i]; +} + +void LayerItem::setProperty( const TQString &name, bool on ) +{ + int i = listView()->d->properties.count() - 1; + while( i && listView()->d->properties[i].name != name ) + --i; + + if( i < 0 || ( isFolder() && !listView()->d->properties[i].validForFolders ) ) + return; + + const bool notify = ( on != d->properties[i] ); + d->properties[i] = on; + if( notify ) + { + emit listView()->propertyChanged( this, name, on ); + emit listView()->propertyChanged( id(), name, on ); + } + + update(); +} + +void LayerItem::toggleProperty( const TQString &name ) +{ + int i = listView()->d->properties.count() - 1; + while( i && listView()->d->properties[i].name != name ) + --i; + + if( i < 0 || ( isFolder() && !listView()->d->properties[i].validForFolders ) ) + return; + + d->properties[i] = !(d->properties[i]); + emit listView()->propertyChanged( this, name, d->properties[i] ); + emit listView()->propertyChanged( id(), name, d->properties[i] ); + + update(); +} + +void LayerItem::setPreviewImage( TQImage *image ) +{ + d->previewImage = image; + previewChanged(); +} + +void LayerItem::previewChanged() +{ + d->previewChanged = true; + update(); +} + +LayerItem *LayerItem::addLayer( const TQString &displayName, LayerItem *after, int id ) +{ + if( !isFolder() ) + return 0; + return new LayerItem( displayName, this, after, id ); +} + +LayerItem *LayerItem::prevSibling() const +{ + LayerItem *item = tqparent() ? tqparent()->firstChild() : listView()->firstChild(); + if( !item || this == item ) + return 0; + for(; item && this != item->nextSibling(); item = item->nextSibling() ); + return item; +} + +int LayerItem::mapXFromListView( int x ) const +{ + return x - rect().left(); +} + +int LayerItem::mapYFromListView( int y ) const +{ + return y - rect().top(); +} + +TQPoint LayerItem::mapFromListView( const TQPoint &point ) const +{ + return TQPoint( mapXFromListView( point.x() ), mapYFromListView( point.y() ) ); +} + +TQRect LayerItem::mapFromListView( const TQRect &rect ) const +{ + return TQRect( mapFromListView( rect.topLeft() ), rect.size() ); +} + +int LayerItem::mapXToListView( int x ) const +{ + return x + rect().left(); +} + +int LayerItem::mapYToListView( int y ) const +{ + return y + rect().top(); +} + +TQPoint LayerItem::mapToListView( const TQPoint &point ) const +{ + return TQPoint( mapXToListView( point.x() ), mapYToListView( point.y() ) ); +} + +TQRect LayerItem::mapToListView( const TQRect &rect ) const +{ + return TQRect( mapToListView( rect.topLeft() ), rect.size() ); +} + +TQRect LayerItem::rect() const +{ + const int indent = listView()->treeStepSize() * ( depth() + 1 ); + return TQRect( listView()->header()->sectionPos( 0 ) + indent, itemPos(), + listView()->header()->sectionSize( 0 ) - indent, height() ); +} + +TQRect LayerItem::textRect() const +{ + static TQFont f; + static int minbearing = 1337 + 666; //can be 0 or negative, 2003 is less likely + if( minbearing == 2003 || f != font() ) + { + f = font(); //getting your bearings can be expensive, so we cache them + minbearing = fontMetrics().minLeftBearing() + fontMetrics().minRightBearing(); + } + + const int margin = listView()->itemMargin(); + int indent = previewRect().right() + margin; + if( pixmap( 0 ) ) + indent += pixmap( 0 )->width() + margin; + + const int width = ( multiline() ? rect().right() : iconsRect().left() ) - indent - margin + minbearing; + + return TQRect( indent, 0, width, fontMetrics().height() ); +} + +TQRect LayerItem::iconsRect() const +{ + const TQValueList<LayerProperty> &lp = listView()->d->properties; + int propscount = 0; + for( int i = 0, n = lp.count(); i < n; ++i ) + if( !lp[i].enabledIcon.isNull() && ( !multiline() || !isFolder() || lp[i].validForFolders ) ) + propscount++; + + const int iconswidth = propscount * iconSize().width() + (propscount - 1) * listView()->itemMargin(); + + const int x = multiline() ? previewRect().right() + listView()->itemMargin() : rect().width() - iconswidth; + const int y = multiline() ? fontMetrics().height() : 0; + + return TQRect( x, y, iconswidth, iconSize().height() ); +} + +TQRect LayerItem::previewRect() const +{ + return TQRect( 0, 0, listView()->previewsShown() ? height() : 0, height() ); +} + +void LayerItem::drawText( TQPainter *p, const TQColorGroup &cg, const TQRect &r ) +{ + p->translate( r.left(), r.top() ); + + p->setPen( isSelected() ? cg.highlightedText() : cg.text() ); + + const TQString text = KStringHandler::rPixelSqueeze( displayName(), p->fontMetrics(), r.width() ); + p->drawText( listView()->itemMargin(), 0, r.width(), r.height(), TQt::AlignAuto | TQt::AlignTop, text ); + + p->translate( -r.left(), -r.top() ); +} + +void LayerItem::drawIcons( TQPainter *p, const TQColorGroup &/*cg*/, const TQRect &r ) +{ + p->translate( r.left(), r.top() ); + + int x = 0; + const TQValueList<LayerProperty> &lp = listView()->d->properties; + for( int i = 0, n = lp.count(); i < n; ++i ) + if( !lp[i].enabledIcon.isNull() && ( !multiline() || !isFolder() || lp[i].validForFolders ) ) + { + if( !isFolder() || lp[i].validForFolders ) + p->drawPixmap( x, 0, d->properties[i] ? lp[i].enabledIcon : lp[i].disabledIcon ); + x += iconSize().width() + listView()->itemMargin(); + } + + p->translate( -r.left(), -r.top() ); +} + +void LayerItem::drawPreview( TQPainter *p, const TQColorGroup &/*cg*/, const TQRect &r ) +{ + if( !showPreview() ) + return; + + if( d->previewChanged || r.size() != d->previewSize ) + { //TODO handle width() != height() + const int size = kMin( r.width(), kMax( previewImage()->width(), previewImage()->height() ) ); + const TQImage i = previewImage()->smoothScale( size, size, TQ_ScaleMin ); + d->scaledPreview.convertFromImage( i ); + d->previewOffset.setX( r.width()/2 - i.width()/2 ); + d->previewOffset.setY( r.height()/2 - i.height()/2 ); + + d->previewChanged = false; + d->previewSize = r.size(); + } + + p->drawPixmap( r.topLeft() + d->previewOffset, d->scaledPreview ); +} + +bool LayerItem::showPreview() const +{ + return listView()->previewsShown() && previewImage() && !previewImage()->isNull(); +} + +bool LayerItem::multiline() const +{ + return height() >= fontMetrics().height() + iconSize().height(); +} + +TQFont LayerItem::font() const +{ + if( isActive() ) + { + TQFont f = listView()->font(); + f.setBold( !f.bold() ); + f.setItalic( !f.italic() ); + return f; + } + else + return listView()->font(); +} + +TQFontMetrics LayerItem::fontMetrics() const +{ + return TQFontMetrics( font() ); +} + +bool LayerItem::mousePressEvent( TQMouseEvent *e ) +{ + if( e->button() == Qt::RightButton ) + { + if ( !(e->state() & TQt::ControlButton) && !(e->state() & TQt::ShiftButton) ) + setActive(); + TQTimer::singleShot( 0, listView(), TQT_SLOT( showContextMenu() ) ); + return false; + } + + const TQRect ir = iconsRect(), tr = textRect(); + + if( ir.tqcontains( e->pos() ) ) + { + const int iconWidth = iconSize().width(); + int x = e->pos().x() - ir.left(); + if( x % ( iconWidth + listView()->itemMargin() ) < iconWidth ) //it's on an icon, not a margin + { + const TQValueList<LayerProperty> &lp = listView()->d->properties; + int p = -1; + for( int i = 0, n = lp.count(); i < n; ++i ) + { + if( !lp[i].enabledIcon.isNull() && ( !multiline() || !isFolder() || lp[i].validForFolders ) ) + x -= iconWidth + listView()->itemMargin(); + p += 1; + if( x < 0 ) + break; + } + toggleProperty( lp[p].name ); + } + return true; + } + + else if( tr.tqcontains( e->pos() ) && isSelected() && !listView()->renameLineEdit()->isVisible() ) + { + listView()->rename( this, 0 ); + TQRect r( listView()->contentsToViewport( mapToListView( tr.topLeft() ) ), tr.size() ); + listView()->renameLineEdit()->setGeometry( r ); + return true; + } + + if ( !(e->state() & TQt::ControlButton) && !(e->state() & TQt::ShiftButton) ) + setActive(); + + return false; +} + +TQString LayerItem::tooltip() const +{ + TQString tip; + tip += "<table cellspacing=\"0\" cellpadding=\"0\">"; + tip += TQString("<tr><td colspan=\"2\" align=\"center\"><b>%1</b></td></tr>").tqarg( displayName() ); + TQString row = "<tr><td>%1</td><td>%2</td></tr>"; + for( int i = 0, n = listView()->d->properties.count(); i < n; ++i ) + if( !isFolder() || listView()->d->properties[i].validForFolders ) + { + if( d->properties[i] ) + tip += row.tqarg( i18n( "%1:" ).tqarg( listView()->d->properties[i].displayName ) ).tqarg( i18n( "Yes" ) ); + else + tip += row.tqarg( i18n( "%1:" ).tqarg( listView()->d->properties[i].displayName ) ).tqarg( i18n( "No" ) ); + } + tip += "</table>"; + return tip; +} + +TQImage *LayerItem::previewImage() const +{ + return d->previewImage; +} + +TQImage LayerItem::tooltipPreview() const +{ + if( previewImage() ) + return *previewImage(); + return TQImage(); +} + +int LayerItem::width( const TQFontMetrics &fm, const TQListView *lv, int c ) const +{ + if( c != 0 ) + return super::width( fm, lv, c ); + + const TQValueList<LayerProperty> &lp = listView()->d->properties; + int propscount = 0; + for( int i = 0, n = d->properties.count(); i < n; ++i ) + if( !lp[i].enabledIcon.isNull() && ( !multiline() || !isFolder() || lp[i].validForFolders ) ) + propscount++; + + const int iconswidth = propscount * iconSize().width() + (propscount - 1) * listView()->itemMargin(); + + if( multiline() ) + return kMax( super::width( fm, lv, 0 ), iconswidth ); + else + return super::width( fm, lv, 0 ) + iconswidth; +} + +void LayerItem::paintCell( TQPainter *painter, const TQColorGroup &cg, int column, int width, int align ) +{ + if( column != 0 ) + { + super::paintCell( painter, cg, column, width, align ); + return; + } + + TQPixmap buf( width, height() ); + TQPainter p( &buf ); + + p.setFont( font() ); + + const TQColorGroup cg_ = isEnabled() ? listView()->tqpalette().active() : listView()->tqpalette().disabled(); + + const TQColor bg = isSelected() ? cg_.highlight() + : isAlternate() ? listView()->alternateBackground() + : listView()->viewport()->backgroundColor(); + + buf.fill( bg ); + + if( pixmap( 0 ) ) + p.drawPixmap( previewRect().right() + listView()->itemMargin(), 0, *pixmap( 0 ) ); + + drawText( &p, cg_, textRect() ); + drawIcons( &p, cg_, iconsRect() ); + drawPreview( &p, cg_, previewRect() ); + + painter->drawPixmap( 0, 0, buf ); +} + +void LayerItem::setup() +{ + super::setup(); + setHeight( listView()->d->itemHeight ); +} + +void LayerItem::setSelected( bool selected ) +{ + if( !selected && ( isActive() || this == listView()->currentItem() ) ) + return; + super::setSelected( selected ); +} + + +///////////////////////// +// Convenience Methods // +///////////////////////// + +LayerItem *LayerList::firstChild() const { return static_cast<LayerItem*>( super::firstChild() ); } +LayerItem *LayerList::lastChild() const { return static_cast<LayerItem*>( super::lastChild() ); } +LayerList *LayerItem::listView() const { return static_cast<LayerList*>( super::listView() ); } +void LayerItem::update() const { listView()->tqrepaintItem( this ); } +LayerItem *LayerItem::firstChild() const { return static_cast<LayerItem*>( super::firstChild() ); } +LayerItem *LayerItem::nextSibling() const { return static_cast<LayerItem*>( super::nextSibling() ); } +LayerItem *LayerItem::tqparent() const { return static_cast<LayerItem*>( super::tqparent() ); } + + +#include "layerlist.moc" |