krita

layerlist.cpp

00001 /*
00002   Copyright (c) 2005 Gábor Lehel <illissius@gmail.com>
00003 
00004   This library is free software; you can redistribute it and/or
00005   modify it under the terms of the GNU Library General Public
00006   License as published by the Free Software Foundation; either
00007   version 2 of the License, or (at your option) any later version.
00008 
00009   This library is distributed in the hope that it will be useful,
00010   but WITHOUT ANY WARRANTY; without even the implied warranty of
00011   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012   Library General Public License for more details.
00013 
00014   You should have received a copy of the GNU Library General Public License
00015   along with this library; see the file COPYING.LIB.  If not, write to
00016   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017   Boston, MA 02110-1301, USA.
00018 */
00019 
00020 
00021 #include "layerlist.h"
00022 
00023 #include <qtooltip.h>
00024 #include <qbitmap.h>
00025 #include <qcursor.h>
00026 #include <qimage.h>
00027 #include <qheader.h>
00028 #include <qpainter.h>
00029 #include <qpixmap.h>
00030 #include <qsimplerichtext.h>
00031 #include <qtimer.h>
00032 
00033 #include <kapplication.h>
00034 #include <kdebug.h>
00035 #include <kglobal.h>
00036 #include <kglobalsettings.h>
00037 #include <kiconloader.h>
00038 #include <klineedit.h>
00039 #include <klocale.h>
00040 #include <kpopupmenu.h>
00041 #include <kstringhandler.h>
00042 
00043 class LayerItemIterator: public QListViewItemIterator
00044 {
00045 public:
00046     LayerItemIterator( LayerList *list ): QListViewItemIterator( list ) { }
00047     LayerItemIterator( LayerList *list, IteratorFlag flags ): QListViewItemIterator( list, flags ) { }
00048     LayerItemIterator( LayerItem *item ): QListViewItemIterator( item ) { }
00049     LayerItemIterator( LayerItem *item, IteratorFlag flags ): QListViewItemIterator( item, flags ) { }
00050     LayerItem *operator*() { return static_cast<LayerItem*>( QListViewItemIterator::operator*() ); }
00051 };
00052 
00053 struct LayerProperty
00054 {
00055     QString name;
00056     QString displayName;
00057     QPixmap enabledIcon;
00058     QPixmap disabledIcon;
00059     bool defaultValue;
00060     bool validForFolders;
00061 
00062     LayerProperty(): defaultValue( false ), validForFolders( true ) { }
00063     LayerProperty( const QString &pname, const QString &pdisplayName, const QPixmap &enabled, const QPixmap &disabled,
00064                    bool pdefaultValue, bool pvalidForFolders )
00065         : name( pname ),
00066           displayName( pdisplayName ),
00067           enabledIcon( enabled ),
00068           disabledIcon( disabled ),
00069           defaultValue( pdefaultValue ),
00070           validForFolders( pvalidForFolders )
00071         { }
00072 };
00073 
00074 class LayerToolTip;
00075 class LayerList::Private
00076 {
00077 public:
00078     LayerItem *activeLayer;
00079     bool foldersCanBeActive;
00080     bool previewsShown;
00081     int itemHeight;
00082     QValueList<LayerProperty> properties;
00083     KPopupMenu contextMenu;
00084     LayerToolTip *tooltip;
00085 
00086     Private( QWidget *parent, LayerList *list );
00087     ~Private();
00088 };
00089 
00090 class LayerItem::Private
00091 {
00092 public:
00093     bool isFolder;
00094     int id;
00095     QValueList<bool> properties;
00096     QImage *previewImage;
00097     bool previewChanged;
00098     QPixmap scaledPreview;
00099     QSize previewSize;
00100     QPoint previewOffset;
00101 
00102     Private( int pid ): isFolder( false ), id( pid ), previewImage( 0 ), previewChanged( false )
00103     { }
00104 };
00105 
00106 static const int MAX_SIZE = 256;
00107 class LayerToolTip: public QToolTip, public QFrame
00108 {
00109     LayerList *m_list;
00110     LayerItem *m_item;
00111     QPoint m_pos;
00112     QTimer m_timer;
00113     QImage m_img;
00114 
00115 public:
00116     LayerToolTip( QWidget *parent, LayerList *list )
00117         : QToolTip( parent ),
00118           QFrame( 0, 0, WStyle_Customize | WStyle_NoBorder | WStyle_Tool | WStyle_StaysOnTop | WX11BypassWM | WNoAutoErase ),
00119           m_list( list )
00120     {
00121         QFrame::setPalette( QToolTip::palette() );
00122         connect( &m_timer, SIGNAL( timeout() ), m_list, SLOT( hideTip() ) );
00123         qApp->installEventFilter( this );
00124     }
00125 
00126     virtual void maybeTip( const QPoint &pos )
00127     {
00128         m_pos = pos;
00129         LayerItem *prev = m_item;
00130         m_item = static_cast<LayerItem*>(m_list->itemAt( m_pos ));
00131         if( QToolTip::parentWidget() && m_list->showToolTips() && m_item )
00132         {
00133             if( m_item != prev )
00134                 hideTip();
00135             showTip();
00136         }
00137         else
00138             hideTip();
00139     }
00140 
00141     void showTip()
00142     {
00143         m_img = m_item->tooltipPreview();
00144         m_timer.start( 15000, true );
00145         if( !isVisible() || sizeHint() != size() )
00146         {
00147             resize( sizeHint() );
00148             position();
00149         }
00150         if( !isVisible() )
00151             show();
00152         else
00153             update();
00154     }
00155 
00156     void hideTip()
00157     {
00158         if( !isVisible() )
00159             return;
00160         QFrame::hide();
00161         QToolTip::hide();
00162         m_timer.stop();
00163         m_img.reset();
00164         m_list->triggerUpdate();
00165     }
00166 
00167     virtual void drawContents( QPainter *painter )
00168     {
00169         QPixmap buf( width(), height() );
00170         QPainter p( &buf );
00171         buf.fill( colorGroup().background() );
00172         p.setPen( colorGroup().foreground() );
00173         p.drawRect( buf.rect() );
00174 
00175         QSimpleRichText text( m_item->tooltip(), QToolTip::font() );
00176         text.setWidth( QCOORD_MAX );
00177 
00178         p.translate( 5, 5 );
00179         if( !m_img.isNull() )
00180         {
00181             if( m_img.width() > MAX_SIZE || m_img.height() > MAX_SIZE )
00182                 m_img = m_img.scale( MAX_SIZE, MAX_SIZE, QImage::ScaleMin );
00183             int y = 0;
00184             if( m_img.height() < text.height() )
00185                 y = text.height()/2 - m_img.height()/2;
00186             p.drawImage( 0, y, m_img );
00187             p.drawRect( -1, y-1, m_img.width()+2, m_img.height()+2 );
00188             p.translate( m_img.width() + 10, 0 );
00189         }
00190 
00191         text.draw( &p, 0, 0, rect(), colorGroup() );
00192 
00193         painter->drawPixmap( 0, 0, buf );
00194     }
00195 
00196     virtual QSize sizeHint() const
00197     {
00198         if( !m_item )
00199             return QSize( 0, 0 );
00200 
00201         QSimpleRichText text( m_item->tooltip(), QToolTip::font() );
00202         text.setWidth( QCOORD_MAX );
00203 
00204         int width = text.widthUsed();
00205         if( !m_img.isNull() )
00206             width += kMin( m_img.width(), MAX_SIZE ) + 10;
00207         width += 10;
00208 
00209         int height = text.height();
00210         if( !m_img.isNull() && kMin( m_img.height(), MAX_SIZE ) > height )
00211             height = kMin( m_img.height(), MAX_SIZE );
00212         height += 10;
00213 
00214         return QSize( width, height );
00215     }
00216 
00217     void position()
00218     {
00219         const QRect drect = QApplication::desktop()->availableGeometry( QToolTip::parentWidget() );
00220         const QSize size = sizeHint();
00221         const int width = size.width(), height = size.height();
00222         const QRect tmp = m_item->rect();
00223         const QRect irect( m_list->viewport()->mapToGlobal( m_list->contentsToViewport(tmp.topLeft()) ), tmp.size() );
00224 
00225         int y;
00226         if( irect.bottom() + height < drect.bottom() )
00227             y = irect.bottom();
00228         else
00229             y = kMax( drect.top(), irect.top() - height );
00230 
00231         int x = kMax( drect.x(), QToolTip::parentWidget()->mapToGlobal( m_pos ).x() - width/2 );
00232         if( x + width > drect.right() )
00233             x = drect.right() - width;
00234 
00235         move( x, y );
00236     }
00237 
00238     virtual bool eventFilter( QObject *, QEvent *e )
00239     {
00240         if( isVisible() )
00241             switch ( e->type() )
00242             {
00243                 case QEvent::KeyPress:
00244                 case QEvent::KeyRelease:
00245                 case QEvent::MouseButtonPress:
00246                 case QEvent::MouseButtonRelease:
00247                 //case QEvent::MouseMove:
00248                 case QEvent::FocusIn:
00249                 case QEvent::FocusOut:
00250                 case QEvent::Wheel:
00251                 case QEvent::Leave:
00252                     hideTip();
00253                 default: break;
00254             }
00255 
00256         return false;
00257     }
00258 };
00259 
00260 LayerList::Private::Private( QWidget *parent, LayerList *list )
00261     : activeLayer( 0 ), foldersCanBeActive( false ), previewsShown( false ), itemHeight( 32 ),
00262       tooltip( new LayerToolTip( parent, list ) ) { }
00263 
00264 LayerList::Private::~Private()
00265 {
00266     delete tooltip;
00267     tooltip = 0;
00268 }
00269 
00270 static int getID()
00271 {
00272     static int id = -2;
00273     return id--;
00274 }
00275 
00276 static QSize iconSize() { return QIconSet::iconSize( QIconSet::Small ); }
00277 
00278 
00280 // LayerList //
00282 
00283 LayerList::LayerList( QWidget *parent, const char *name )
00284     : super( parent, name ), d( new Private( viewport(), this ) )
00285 {
00286     setSelectionMode( QListView::Extended );
00287     setRootIsDecorated( true );
00288     setSorting( -1 );
00289     setSortColumn( -1 );
00290     setAllColumnsShowFocus( true );
00291     setFullWidth( true );
00292     setItemsRenameable( false );
00293     setDropHighlighter( true );
00294     setDefaultRenameAction( QListView::Accept );
00295     setDragEnabled( true );
00296     setAcceptDrops( true );
00297     setItemsMovable( true );
00298     addColumn( QString() );
00299     header()->hide();
00300 
00301     QToolTip::add(this, i18n("Right-click to create folders. Click on the layername to change the layer's name. Click and drag to move layers."));
00302 
00303     setNumRows( 2 );
00304 
00305     connect( this, SIGNAL( itemRenamed( QListViewItem*, const QString&, int ) ),
00306                  SLOT( slotItemRenamed( QListViewItem*, const QString&, int ) ) );
00307     connect( this, SIGNAL( moved( QPtrList<QListViewItem>&, QPtrList<QListViewItem>&, QPtrList<QListViewItem>& ) ),
00308              SLOT( slotItemMoved( QPtrList<QListViewItem>&, QPtrList<QListViewItem>&, QPtrList<QListViewItem>& ) ) );
00309     connect( this, SIGNAL( onItem( QListViewItem* ) ), SLOT( hideTip() ) );
00310     connect( this, SIGNAL( onViewport() ), SLOT( hideTip() ) );
00311 }
00312 
00313 LayerList::~LayerList()
00314 {
00315     delete d;
00316 }
00317 
00318 void LayerList::addProperty( const QString &name, const QString &displayName, const QIconSet &icon,
00319                              bool defaultValue, bool validForFolders )
00320 {
00321     addProperty( name, displayName, icon.pixmap( QIconSet::Small, QIconSet::Normal ), icon.pixmap( QIconSet::Small, QIconSet::Disabled ), defaultValue, validForFolders );
00322 }
00323 
00324 void LayerList::addProperty( const QString &name, const QString &displayName, QPixmap enabled, QPixmap disabled,
00325                              bool defaultValue, bool validForFolders )
00326 {
00327     d->properties.append( LayerProperty( name, displayName, enabled, disabled, defaultValue, validForFolders ) );
00328 
00329     for( LayerItemIterator it( this ); *it; ++it )
00330         (*it)->d->properties.append( defaultValue );
00331 
00332     //we do this only afterwards in case someone wants to access the other items in a connected slot...
00333     for( LayerItemIterator it( this ); *it; ++it )
00334         if( validForFolders || !(*it)->isFolder() )
00335         {
00336             emit propertyChanged( *it, name, defaultValue );
00337             emit propertyChanged( (*it)->id(), name, defaultValue );
00338         }
00339 
00340     triggerUpdate();
00341 }
00342 
00343 LayerItem *LayerList::layer( int id ) const
00344 {
00345     if( !firstChild() || id == -1 )
00346         return 0;
00347 
00348     for( LayerItemIterator it( firstChild() ); *it; ++it )
00349         if( (*it)->id() == id )
00350             return (*it);
00351 
00352     return 0;
00353 }
00354 
00355 LayerItem *LayerList::folder( int id ) const
00356 {
00357     if( !firstChild() || id == -1 )
00358         return 0;
00359 
00360     for( LayerItemIterator it( firstChild() ); *it; ++it )
00361         if( (*it)->id() == id && (*it)->isFolder() )
00362             return (*it);
00363 
00364     return 0;
00365 }
00366 
00367 LayerItem *LayerList::activeLayer() const
00368 {
00369     return d->activeLayer;
00370 }
00371 
00372 int LayerList::activeLayerID() const
00373 {
00374     if( activeLayer() )
00375         return activeLayer()->id();
00376     return -1;
00377 }
00378 
00379 QValueList<LayerItem*> LayerList::selectedLayers() const
00380 {
00381     if( !firstChild() )
00382         return QValueList<LayerItem*>();
00383 
00384     QValueList<LayerItem*> layers;
00385     for( LayerItemIterator it( firstChild() ); *it; ++it )
00386         if( (*it)->isSelected() )
00387             layers.append( *it );
00388 
00389     return layers;
00390 }
00391 
00392 QValueList<int> LayerList::selectedLayerIDs() const
00393 {
00394     const QValueList<LayerItem*> layers = selectedLayers();
00395     QValueList<int> ids;
00396     for( int i = 0, n = layers.count(); i < n; ++i )
00397         ids.append( layers[i]->id() );
00398 
00399     return ids;
00400 }
00401 
00402 bool LayerList::foldersCanBeActive() const
00403 {
00404     return d->foldersCanBeActive;
00405 }
00406 
00407 bool LayerList::previewsShown() const
00408 {
00409     return d->previewsShown;
00410 }
00411 
00412 int LayerList::itemHeight() const
00413 {
00414     return d->itemHeight;
00415 }
00416 
00417 int LayerList::numRows() const
00418 {
00419     if( itemHeight() < kMax( fontMetrics().height(), iconSize().height() ) )
00420         return 0;
00421 
00422     return ( itemHeight() - fontMetrics().height() ) / iconSize().height() + 1;
00423 }
00424 
00425 void LayerList::makeFolder( int id )
00426 {
00427     LayerItem* const l = layer( id );
00428     if( l )
00429         l->makeFolder();
00430 }
00431 
00432 bool LayerList::isFolder( int id ) const
00433 {
00434     LayerItem* const l = layer( id );
00435     if( !l )
00436         return false;
00437 
00438     return l->isFolder();
00439 }
00440 
00441 QString LayerList::displayName( int id ) const
00442 {
00443     LayerItem* const l = layer( id );
00444     if( !l )
00445         return QString::null; //should be more severe...
00446 
00447     return l->displayName();
00448 }
00449 
00450 bool LayerList::property( int id, const QString &name ) const
00451 {
00452     LayerItem* const l = layer( id );
00453     if( !l )
00454         return false; //should be more severe...
00455 
00456     return l->property( name );
00457 }
00458 
00459 KPopupMenu *LayerList::contextMenu() const
00460 {
00461     return &( d->contextMenu );
00462 }
00463 
00464 void LayerList::setFoldersCanBeActive( bool can ) //SLOT
00465 {
00466     d->foldersCanBeActive = can;
00467     if( !can && activeLayer() && activeLayer()->isFolder() )
00468     {
00469         d->activeLayer = 0;
00470         emit activated( static_cast<LayerItem*>( 0 ) );
00471         emit activated( -1 );
00472     }
00473 }
00474 
00475 void LayerList::setPreviewsShown( bool show ) //SLOT
00476 {
00477     d->previewsShown = show;
00478     triggerUpdate();
00479 }
00480 
00481 void LayerList::setItemHeight( int height ) //SLOT
00482 {
00483     d->itemHeight = height;
00484     for( LayerItemIterator it( this ); *it; ++it )
00485         (*it)->setup();
00486     triggerUpdate();
00487 }
00488 
00489 void LayerList::setNumRows( int rows )
00490 {
00491     if( rows < 1 )
00492         return;
00493 
00494     if( rows == 1 )
00495         setItemHeight( kMax( fontMetrics().height(), iconSize().height() ) );
00496     else
00497         setItemHeight( fontMetrics().height() + ( rows - 1 ) * iconSize().height() );
00498 }
00499 
00500 void LayerList::setActiveLayer( LayerItem *layer ) //SLOT
00501 {
00502     if( !foldersCanBeActive() && layer && layer->isFolder() )
00503         return;
00504 
00505     ensureItemVisible( layer );
00506 
00507     if( d->activeLayer == layer )
00508         return;
00509 
00510     d->activeLayer = layer;
00511 
00512     if( currentItem() != layer )
00513         setCurrentItem( layer );
00514     else
00515     {
00516         int n = 0;
00517         for( LayerItemIterator it( this, LayerItemIterator::Selected ); n < 2 && (*it); ++it ) { n++; }
00518         if( n == 1 )
00519             (*LayerItemIterator( this, LayerItemIterator::Selected ))->setSelected( false );
00520         if( layer )
00521             layer->setSelected( true );
00522     }
00523 
00524     emit activated( layer );
00525     if( layer )
00526         emit activated( layer->id() );
00527     else
00528         emit activated( -1 );
00529 }
00530 
00531 void LayerList::setActiveLayer( int id ) //SLOT
00532 {
00533     setActiveLayer( layer( id ) );
00534 }
00535 
00536 void LayerList::setLayerDisplayName( LayerItem *layer, const QString &displayName )
00537 {
00538     if( !layer )
00539         return;
00540 
00541     layer->setDisplayName( displayName );
00542 }
00543 
00544 void LayerList::setLayerDisplayName( int id, const QString &displayName )
00545 {
00546     setLayerDisplayName( layer( id ), displayName );
00547 }
00548 
00549 void LayerList::setLayerProperty( LayerItem *layer, const QString &name, bool on ) //SLOT
00550 {
00551     if( !layer )
00552         return;
00553 
00554     layer->setProperty( name, on );
00555 }
00556 
00557 void LayerList::setLayerProperty( int id, const QString &name, bool on ) //SLOT
00558 {
00559     setLayerProperty( layer( id ), name, on );
00560 }
00561 
00562 void LayerList::toggleLayerProperty( LayerItem *layer, const QString &name ) //SLOT
00563 {
00564     if( !layer )
00565         return;
00566 
00567     layer->toggleProperty( name );
00568 }
00569 
00570 void LayerList::toggleLayerProperty( int id, const QString &name ) //SLOT
00571 {
00572     toggleLayerProperty( layer( id ), name );
00573 }
00574 
00575 void LayerList::setLayerPreviewImage( LayerItem *layer, QImage *image )
00576 {
00577     if( !layer )
00578         return;
00579 
00580     layer->setPreviewImage( image );
00581 }
00582 
00583 void LayerList::setLayerPreviewImage( int id, QImage *image )
00584 {
00585     setLayerPreviewImage( layer( id ), image );
00586 }
00587 
00588 void LayerList::layerPreviewChanged( LayerItem *layer )
00589 {
00590     if( !layer )
00591         return;
00592 
00593     layer->previewChanged();
00594 }
00595 
00596 void LayerList::layerPreviewChanged( int id )
00597 {
00598     layerPreviewChanged( layer( id ) );
00599 }
00600 
00601 LayerItem *LayerList::addLayer( const QString &displayName, LayerItem *after, int id ) //SLOT
00602 {
00603     return new LayerItem( displayName, this, after, id );
00604 }
00605 
00606 LayerItem *LayerList::addLayer( const QString &displayName, int afterID, int id ) //SLOT
00607 {
00608     return new LayerItem( displayName, this, layer( afterID ), id );
00609 }
00610 
00611 //SLOT
00612 LayerItem *LayerList::addLayerToParent( const QString &displayName, LayerItem *parent, LayerItem *after, int id )
00613 {
00614     if( parent && parent->isFolder() )
00615         return parent->addLayer( displayName, after, id );
00616     else
00617         return 0;
00618 }
00619 
00620 LayerItem *LayerList::addLayerToParent( const QString &displayName, int parentID, int afterID, int id ) //SLOT
00621 {
00622     return addLayerToParent( displayName, folder( parentID ), layer( afterID ), id );
00623 }
00624 
00625 void LayerList::moveLayer( LayerItem *layer, LayerItem *parent, LayerItem *after ) //SLOT
00626 {
00627     if( !layer )
00628         return;
00629 
00630     if( parent && !parent->isFolder() )
00631         parent = 0;
00632 
00633     if( layer->parent() == parent && layer->prevSibling() == after )
00634         return;
00635 
00636     QListViewItem *current = currentItem();
00637 
00638     moveItem( layer, parent, after );
00639 
00640     emit layerMoved( layer, parent, after );
00641     emit layerMoved( layer->id(), parent ? parent->id() : -1, after ? after->id() : -1 );
00642 
00643     setCurrentItem( current ); //HACK, sometimes Qt changes this under us
00644 }
00645 
00646 void LayerList::moveLayer( int id, int parentID, int afterID ) //SLOT
00647 {
00648     moveLayer( layer( id ), folder( parentID ), layer( afterID ) );
00649 }
00650 
00651 void LayerList::removeLayer( LayerItem *layer ) //SLOT
00652 {
00653     delete layer;
00654 }
00655 
00656 void LayerList::removeLayer( int id ) //SLOT
00657 {
00658     delete layer( id );
00659 }
00660 
00661 void LayerList::contentsMousePressEvent( QMouseEvent *e )
00662 {
00663     LayerItem *item = static_cast<LayerItem*>( itemAt( contentsToViewport( e->pos() ) ) );
00664 
00665     if( item )
00666     {
00667         QMouseEvent m( QEvent::MouseButtonPress, item->mapFromListView( e->pos() ), e->button(), e->state() );
00668         if( !item->mousePressEvent( &m ) )
00669             super::contentsMousePressEvent( e );
00670     }
00671     else
00672     {
00673         super::contentsMousePressEvent( e );
00674         if( e->button() == Qt::RightButton )
00675             showContextMenu();
00676     }
00677 }
00678 
00679 void LayerList::contentsMouseDoubleClickEvent( QMouseEvent *e )
00680 {
00681     super::contentsMouseDoubleClickEvent( e );
00682     if( LayerItem *layer = static_cast<LayerItem*>( itemAt( contentsToViewport( e->pos() ) ) ) )
00683     {
00684         if( !layer->iconsRect().contains( layer->mapFromListView( e->pos() ) ) )
00685         {
00686             emit requestLayerProperties( layer );
00687             emit requestLayerProperties( layer->id() );
00688         }
00689     }
00690     else
00691     {
00692         emit requestNewLayer( static_cast<LayerItem*>( 0 ), static_cast<LayerItem*>( 0 ) );
00693         emit requestNewLayer( -1, -1 );
00694     }
00695 }
00696 
00697 void LayerList::findDrop( const QPoint &pos, QListViewItem *&parent, QListViewItem *&after )
00698 {
00699     LayerItem *item = static_cast<LayerItem*>( itemAt( contentsToViewport( pos ) ) );
00700     if( item && item->isFolder() )
00701     {
00702         parent = item;
00703         after = 0;
00704     }
00705     else
00706         super::findDrop( pos, parent, after );
00707 }
00708 
00709 void LayerList::showContextMenu()
00710 {
00711     LayerItem *layer = static_cast<LayerItem*>( itemAt( viewport()->mapFromGlobal( QCursor::pos() ) ) );
00712     if( layer )
00713         setCurrentItem( layer );
00714     d->contextMenu.clear();
00715     constructMenu( layer );
00716     menuActivated( d->contextMenu.exec( QCursor::pos() ), layer );
00717 }
00718 
00719 void LayerList::hideTip()
00720 {
00721     d->tooltip->hideTip();
00722 }
00723 
00724 void LayerList::maybeTip()
00725 {
00726     d->tooltip->maybeTip( d->tooltip->QToolTip::parentWidget()->mapFromGlobal( QCursor::pos() ) );
00727 }
00728 
00729 void LayerList::constructMenu( LayerItem *layer )
00730 {
00731     if( layer )
00732     {
00733         for( int i = 0, n = d->properties.count(); i < n; ++i )
00734             if( !layer->isFolder() || d->properties[i].validForFolders )
00735                 d->contextMenu.insertItem( layer->d->properties[i] ? d->properties[i].enabledIcon : d->properties[i].disabledIcon, d->properties[i].displayName, MenuItems::COUNT + i );
00736         d->contextMenu.insertItem( SmallIconSet( "info" ), i18n( "&Properties" ), MenuItems::LayerProperties );
00737         d->contextMenu.insertSeparator();
00738         d->contextMenu.insertItem( SmallIconSet( "editdelete" ),
00739             selectedLayers().count() > 1 ? i18n( "Remove Layers" )
00740                    : layer->isFolder() ? i18n( "&Remove Folder" )
00741                                        : i18n( "&Remove Layer" ), MenuItems::RemoveLayer );
00742     }
00743     d->contextMenu.insertItem( SmallIconSet( "filenew" ), i18n( "&New Layer" ), MenuItems::NewLayer );
00744     d->contextMenu.insertItem( SmallIconSet( "folder" ), i18n( "New &Folder" ), MenuItems::NewFolder );
00745 }
00746 
00747 void LayerList::menuActivated( int id, LayerItem *layer )
00748 {
00749     const QValueList<LayerItem*> selected = selectedLayers();
00750 
00751     LayerItem *parent = ( layer && layer->isFolder() ) ? layer : 0;
00752     LayerItem *after = 0;
00753     if( layer && !parent )
00754     {
00755         parent = layer->parent();
00756         after = layer->prevSibling();
00757     }
00758     switch( id )
00759     {
00760         case MenuItems::NewLayer:
00761             emit requestNewLayer( parent, after );
00762             emit requestNewLayer( parent ? parent->id() : -1, after ? after->id() : -1 );
00763             break;
00764         case MenuItems::NewFolder:
00765             emit requestNewFolder( parent, after );
00766             emit requestNewFolder( parent ? parent->id() : -1, after ? after->id() : -1 );
00767             break;
00768         case MenuItems::RemoveLayer:
00769             {
00770                 QValueList<int> ids;
00771                 for( int i = 0, n = selected.count(); i < n; ++i )
00772                 {
00773                     ids.append( selected[i]->id() );
00774                     emit requestRemoveLayer( selected[i]->id() );
00775                 }
00776                 emit requestRemoveLayers( ids );
00777             }
00778             for( int i = 0, n = selected.count(); i < n; ++i )
00779                 emit requestRemoveLayer( selected[i] );
00780             emit requestRemoveLayers( selected );
00781             break;
00782         case MenuItems::LayerProperties:
00783             if( layer )
00784             {
00785                 emit requestLayerProperties( layer );
00786                 emit requestLayerProperties( layer->id() );
00787             }
00788             break;
00789         default:
00790             if( id >= MenuItems::COUNT && layer )
00791                 for( int i = 0, n = selected.count(); i < n; ++i )
00792                     selected[i]->toggleProperty( d->properties[ id - MenuItems::COUNT ].name );
00793     }
00794 }
00795 
00796 void LayerList::slotItemRenamed( QListViewItem *item, const QString &text, int col )
00797 {
00798     if( !item || col != 0 )
00799         return;
00800 
00801     emit displayNameChanged( static_cast<LayerItem*>( item ), text );
00802     emit displayNameChanged( static_cast<LayerItem*>( item )->id(), text );
00803 }
00804 
00805 void LayerList::slotItemMoved( QPtrList<QListViewItem> &items, QPtrList<QListViewItem> &/*afterBefore*/, QPtrList<QListViewItem> &afterNow )
00806 {
00807     for( int i = 0, n = items.count(); i < n; ++i )
00808     {
00809         LayerItem *l = static_cast<LayerItem*>( items.at(i) ), *a = static_cast<LayerItem*>( afterNow.at(i) );
00810         if( !l )
00811             continue;
00812 
00813         if( l->parent() )
00814             l->parent()->setOpen( true );
00815 
00816         emit layerMoved( l, l->parent(), a );
00817         emit layerMoved( l->id(), l->parent() ? l->parent()->id() : -1, a ? a->id() : -1 );
00818     }
00819 }
00820 
00821 void LayerList::setCurrentItem( QListViewItem *item )
00822 {
00823     if( !item )
00824         return;
00825 
00826     super::setCurrentItem( item );
00827     ensureItemVisible( item );
00828     int n = 0;
00829     for( LayerItemIterator it( this, LayerItemIterator::Selected ); n < 2 && (*it); ++it ) { n++; }
00830     if( n == 1 )
00831         (*LayerItemIterator( this, LayerItemIterator::Selected ))->setSelected( false );
00832     item->setSelected( true );
00833     if( activeLayer() != item )
00834         setActiveLayer( static_cast<LayerItem*>(item) );
00835 }
00836 
00837 
00839 // LayerItem //
00841 
00842 LayerItem::LayerItem( const QString &displayName, LayerList *p, LayerItem *after, int id )
00843     : super( p, after ), d( new Private( id ) )
00844 {
00845     init();
00846     setDisplayName( displayName );
00847 }
00848 
00849 LayerItem::LayerItem( const QString &displayName, LayerItem *p, LayerItem *after, int id )
00850     : super( ( p && p->isFolder() ) ? p : 0, after ), d( new Private( id ) )
00851 {
00852     init();
00853     setDisplayName( displayName );
00854 }
00855 
00856 void LayerItem::init()
00857 {
00858     if( d->id < 0 )
00859         d->id = getID();
00860 
00861     for( int i = 0, n = listView()->d->properties.count(); i < n; ++i )
00862         d->properties.append( listView()->d->properties[i].defaultValue );
00863 
00864     if( parent())
00865         parent()->setOpen( true );
00866 }
00867 
00868 LayerItem::~LayerItem()
00869 {
00870     if (listView() && (listView()->activeLayer() == this || contains(listView()->activeLayer())))
00871         listView()->setActiveLayer( static_cast<LayerItem*>( 0 ) );
00872     delete d;
00873 }
00874 
00875 void LayerItem::makeFolder()
00876 {
00877     d->isFolder = true;
00878     setPixmap( 0, SmallIcon( "folder", 16 ) );
00879     if( isActive() && !listView()->foldersCanBeActive() )
00880         listView()->setActiveLayer( static_cast<LayerItem*>( 0 ) );
00881 }
00882 
00883 bool LayerItem::isFolder() const
00884 {
00885     return d->isFolder;
00886 }
00887 
00888 bool LayerItem::contains(const LayerItem *item)
00889 {
00890     QListViewItemIterator it(this);
00891 
00892     while (it.current()) {
00893         if (static_cast<const LayerItem *>(it.current()) == item) {
00894             return true;
00895         }
00896         ++it;
00897     }
00898     return false;
00899 }
00900 
00901 int LayerItem::id() const
00902 {
00903     return d->id;
00904 }
00905 
00906 QString LayerItem::displayName() const
00907 {
00908     return text( 0 );
00909 }
00910 
00911 void LayerItem::setDisplayName( const QString &s )
00912 {
00913     if( displayName() == s )
00914         return;
00915     setText( 0, s );
00916     emit listView()->displayNameChanged( this, s );
00917     emit listView()->displayNameChanged( id(), s );
00918 }
00919 
00920 bool LayerItem::isActive() const
00921 {
00922     return listView()->activeLayer() == this;
00923 }
00924 
00925 void LayerItem::setActive()
00926 {
00927     listView()->setActiveLayer( this );
00928 }
00929 
00930 bool LayerItem::property( const QString &name ) const
00931 {
00932     int i = listView()->d->properties.count() - 1;
00933     while( i && listView()->d->properties[i].name != name )
00934         --i;
00935 
00936     if( i < 0 )
00937         return false; //should do something more severe... but what?
00938 
00939     return d->properties[i];
00940 }
00941 
00942 void LayerItem::setProperty( const QString &name, bool on )
00943 {
00944     int i = listView()->d->properties.count() - 1;
00945     while( i && listView()->d->properties[i].name != name )
00946         --i;
00947 
00948     if( i < 0 || ( isFolder() && !listView()->d->properties[i].validForFolders ) )
00949         return;
00950 
00951     const bool notify = ( on != d->properties[i] );
00952     d->properties[i] = on;
00953     if( notify )
00954     {
00955         emit listView()->propertyChanged( this, name, on );
00956         emit listView()->propertyChanged( id(), name, on );
00957     }
00958 
00959     update();
00960 }
00961 
00962 void LayerItem::toggleProperty( const QString &name )
00963 {
00964     int i = listView()->d->properties.count() - 1;
00965     while( i && listView()->d->properties[i].name != name )
00966         --i;
00967 
00968     if( i < 0 || ( isFolder() && !listView()->d->properties[i].validForFolders ) )
00969         return;
00970 
00971     d->properties[i] = !(d->properties[i]);
00972     emit listView()->propertyChanged( this, name, d->properties[i] );
00973     emit listView()->propertyChanged( id(), name, d->properties[i] );
00974 
00975     update();
00976 }
00977 
00978 void LayerItem::setPreviewImage( QImage *image )
00979 {
00980     d->previewImage = image;
00981     previewChanged();
00982 }
00983 
00984 void LayerItem::previewChanged()
00985 {
00986     d->previewChanged = true;
00987     update();
00988 }
00989 
00990 LayerItem *LayerItem::addLayer( const QString &displayName, LayerItem *after, int id )
00991 {
00992     if( !isFolder() )
00993         return 0;
00994     return new LayerItem( displayName, this, after, id );
00995 }
00996 
00997 LayerItem *LayerItem::prevSibling() const
00998 {
00999     LayerItem *item = parent() ? parent()->firstChild() : listView()->firstChild();
01000     if( !item || this == item )
01001         return 0;
01002     for(; item && this != item->nextSibling(); item = item->nextSibling() );
01003     return item;
01004 }
01005 
01006 int LayerItem::mapXFromListView( int x ) const
01007 {
01008     return x - rect().left();
01009 }
01010 
01011 int LayerItem::mapYFromListView( int y ) const
01012 {
01013     return y - rect().top();
01014 }
01015 
01016 QPoint LayerItem::mapFromListView( const QPoint &point ) const
01017 {
01018     return QPoint( mapXFromListView( point.x() ), mapYFromListView( point.y() ) );
01019 }
01020 
01021 QRect LayerItem::mapFromListView( const QRect &rect ) const
01022 {
01023     return QRect( mapFromListView( rect.topLeft() ), rect.size() );
01024 }
01025 
01026 int LayerItem::mapXToListView( int x ) const
01027 {
01028     return x + rect().left();
01029 }
01030 
01031 int LayerItem::mapYToListView( int y ) const
01032 {
01033     return y + rect().top();
01034 }
01035 
01036 QPoint LayerItem::mapToListView( const QPoint &point ) const
01037 {
01038     return QPoint( mapXToListView( point.x() ), mapYToListView( point.y() ) );
01039 }
01040 
01041 QRect LayerItem::mapToListView( const QRect &rect ) const
01042 {
01043     return QRect( mapToListView( rect.topLeft() ), rect.size() );
01044 }
01045 
01046 QRect LayerItem::rect() const
01047 {
01048     const int indent = listView()->treeStepSize() * ( depth() + 1 );
01049     return QRect( listView()->header()->sectionPos( 0 )  + indent, itemPos(),
01050                   listView()->header()->sectionSize( 0 ) - indent, height() );
01051 }
01052 
01053 QRect LayerItem::textRect() const
01054 {
01055     static QFont f;
01056     static int minbearing = 1337 + 666; //can be 0 or negative, 2003 is less likely
01057     if( minbearing == 2003 || f != font() )
01058     {
01059         f = font(); //getting your bearings can be expensive, so we cache them
01060         minbearing = fontMetrics().minLeftBearing() + fontMetrics().minRightBearing();
01061     }
01062 
01063     const int margin = listView()->itemMargin();
01064     int indent = previewRect().right() + margin;
01065     if( pixmap( 0 ) )
01066         indent += pixmap( 0 )->width() + margin;
01067 
01068     const int width = ( multiline() ? rect().right() : iconsRect().left() ) - indent - margin + minbearing;
01069 
01070     return QRect( indent, 0, width, fontMetrics().height() );
01071 }
01072 
01073 QRect LayerItem::iconsRect() const
01074 {
01075     const QValueList<LayerProperty> &lp = listView()->d->properties;
01076     int propscount = 0;
01077     for( int i = 0, n = lp.count(); i < n; ++i )
01078         if( !lp[i].enabledIcon.isNull() && ( !multiline() || !isFolder() || lp[i].validForFolders ) )
01079             propscount++;
01080 
01081     const int iconswidth = propscount * iconSize().width() + (propscount - 1) * listView()->itemMargin();
01082 
01083     const int x = multiline() ? previewRect().right() + listView()->itemMargin() : rect().width() - iconswidth;
01084     const int y = multiline() ? fontMetrics().height() : 0;
01085 
01086     return QRect( x, y, iconswidth, iconSize().height() );
01087 }
01088 
01089 QRect LayerItem::previewRect() const
01090 {
01091     return QRect( 0, 0, listView()->previewsShown() ? height() : 0, height() );
01092 }
01093 
01094 void LayerItem::drawText( QPainter *p, const QColorGroup &cg, const QRect &r )
01095 {
01096     p->translate( r.left(), r.top() );
01097 
01098     p->setPen( isSelected() ? cg.highlightedText() : cg.text() );
01099 
01100     const QString text = KStringHandler::rPixelSqueeze( displayName(), p->fontMetrics(), r.width() );
01101     p->drawText( listView()->itemMargin(), 0, r.width(), r.height(), Qt::AlignAuto | Qt::AlignTop, text );
01102 
01103     p->translate( -r.left(), -r.top() );
01104 }
01105 
01106 void LayerItem::drawIcons( QPainter *p, const QColorGroup &/*cg*/, const QRect &r )
01107 {
01108     p->translate( r.left(), r.top() );
01109 
01110     int x = 0;
01111     const QValueList<LayerProperty> &lp = listView()->d->properties;
01112     for( int i = 0, n = lp.count(); i < n; ++i )
01113         if( !lp[i].enabledIcon.isNull() && ( !multiline() || !isFolder() || lp[i].validForFolders ) )
01114         {
01115             if( !isFolder() || lp[i].validForFolders )
01116                 p->drawPixmap( x, 0, d->properties[i] ? lp[i].enabledIcon : lp[i].disabledIcon );
01117             x += iconSize().width() + listView()->itemMargin();
01118         }
01119 
01120     p->translate( -r.left(), -r.top() );
01121 }
01122 
01123 void LayerItem::drawPreview( QPainter *p, const QColorGroup &/*cg*/, const QRect &r )
01124 {
01125     if( !showPreview() )
01126         return;
01127 
01128     if( d->previewChanged || r.size() != d->previewSize )
01129     {      //TODO handle width() != height()
01130         const int size = kMin( r.width(), kMax( previewImage()->width(), previewImage()->height() ) );
01131         const QImage i = previewImage()->smoothScale( size, size, QImage::ScaleMin );
01132         d->scaledPreview.convertFromImage( i );
01133         d->previewOffset.setX( r.width()/2 - i.width()/2 );
01134         d->previewOffset.setY( r.height()/2 - i.height()/2 );
01135 
01136         d->previewChanged = false;
01137         d->previewSize = r.size();
01138     }
01139 
01140     p->drawPixmap( r.topLeft() + d->previewOffset, d->scaledPreview );
01141 }
01142 
01143 bool LayerItem::showPreview() const
01144 {
01145     return listView()->previewsShown() && previewImage() && !previewImage()->isNull();
01146 }
01147 
01148 bool LayerItem::multiline() const
01149 {
01150     return height() >= fontMetrics().height() + iconSize().height();
01151 }
01152 
01153 QFont LayerItem::font() const
01154 {
01155     if( isActive() )
01156     {
01157         QFont f = listView()->font();
01158         f.setBold( !f.bold() );
01159         f.setItalic( !f.italic() );
01160         return f;
01161     }
01162     else
01163         return listView()->font();
01164 }
01165 
01166 QFontMetrics LayerItem::fontMetrics() const
01167 {
01168     return QFontMetrics( font() );
01169 }
01170 
01171 bool LayerItem::mousePressEvent( QMouseEvent *e )
01172 {
01173     if( e->button() == Qt::RightButton )
01174     {
01175         if ( !(e->state() & Qt::ControlButton) && !(e->state() & Qt::ShiftButton) )
01176             setActive();
01177         QTimer::singleShot( 0, listView(), SLOT( showContextMenu() ) );
01178         return false;
01179     }
01180 
01181     const QRect ir = iconsRect(), tr = textRect();
01182 
01183     if( ir.contains( e->pos() ) )
01184     {
01185         const int iconWidth = iconSize().width();
01186         int x = e->pos().x() - ir.left();
01187         if( x % ( iconWidth + listView()->itemMargin() ) < iconWidth ) //it's on an icon, not a margin
01188         {
01189             const QValueList<LayerProperty> &lp = listView()->d->properties;
01190             int p = -1;
01191             for( int i = 0, n = lp.count(); i < n; ++i )
01192             {
01193                 if( !lp[i].enabledIcon.isNull() && ( !multiline() || !isFolder() || lp[i].validForFolders ) )
01194                     x -= iconWidth + listView()->itemMargin();
01195                 p += 1;
01196                 if( x < 0 )
01197                     break;
01198             }
01199             toggleProperty( lp[p].name );
01200         }
01201         return true;
01202     }
01203 
01204     else if( tr.contains( e->pos() ) && isSelected() && !listView()->renameLineEdit()->isVisible() )
01205     {
01206         listView()->rename( this, 0 );
01207         QRect r( listView()->contentsToViewport( mapToListView( tr.topLeft() ) ), tr.size() );
01208         listView()->renameLineEdit()->setGeometry( r );
01209         return true;
01210     }
01211 
01212     if ( !(e->state() & Qt::ControlButton) && !(e->state() & Qt::ShiftButton) )
01213         setActive();
01214 
01215     return false;
01216 }
01217 
01218 QString LayerItem::tooltip() const
01219 {
01220     QString tip;
01221     tip += "<table cellspacing=\"0\" cellpadding=\"0\">";
01222     tip += QString("<tr><td colspan=\"2\" align=\"center\"><b>%1</b></td></tr>").arg( displayName() );
01223     QString row = "<tr><td>%1</td><td>%2</td></tr>";
01224     for( int i = 0, n = listView()->d->properties.count(); i < n; ++i )
01225         if( !isFolder() || listView()->d->properties[i].validForFolders )
01226         {
01227             if( d->properties[i] )
01228                 tip += row.arg( i18n( "%1:" ).arg( listView()->d->properties[i].displayName ) ).arg( i18n( "Yes" ) );
01229             else
01230                 tip += row.arg( i18n( "%1:" ).arg( listView()->d->properties[i].displayName ) ).arg( i18n( "No" ) );
01231         }
01232     tip += "</table>";
01233     return tip;
01234 }
01235 
01236 QImage *LayerItem::previewImage() const
01237 {
01238     return d->previewImage;
01239 }
01240 
01241 QImage LayerItem::tooltipPreview() const
01242 {
01243     if( previewImage() )
01244         return *previewImage();
01245     return QImage();
01246 }
01247 
01248 int LayerItem::width( const QFontMetrics &fm, const QListView *lv, int c ) const
01249 {
01250     if( c != 0 )
01251         return super::width( fm, lv, c );
01252 
01253     const QValueList<LayerProperty> &lp = listView()->d->properties;
01254     int propscount = 0;
01255     for( int i = 0, n = d->properties.count(); i < n; ++i )
01256         if( !lp[i].enabledIcon.isNull() && ( !multiline() || !isFolder() || lp[i].validForFolders ) )
01257             propscount++;
01258 
01259     const int iconswidth = propscount * iconSize().width() + (propscount - 1) * listView()->itemMargin();
01260 
01261     if( multiline() )
01262         return kMax( super::width( fm, lv, 0 ), iconswidth );
01263     else
01264         return super::width( fm, lv, 0 ) + iconswidth;
01265 }
01266 
01267 void LayerItem::paintCell( QPainter *painter, const QColorGroup &cg, int column, int width, int align )
01268 {
01269     if( column != 0 )
01270     {
01271         super::paintCell( painter, cg, column, width, align );
01272         return;
01273     }
01274 
01275     QPixmap buf( width, height() );
01276     QPainter p( &buf );
01277 
01278     p.setFont( font() );
01279 
01280     const QColorGroup cg_ = isEnabled() ? listView()->palette().active() : listView()->palette().disabled();
01281 
01282     const QColor bg = isSelected()  ? cg_.highlight()
01283                     : isAlternate() ? listView()->alternateBackground()
01284                     : listView()->viewport()->backgroundColor();
01285 
01286     buf.fill( bg );
01287 
01288     if( pixmap( 0 ) )
01289         p.drawPixmap( previewRect().right() + listView()->itemMargin(), 0, *pixmap( 0 ) );
01290 
01291     drawText( &p, cg_, textRect() );
01292     drawIcons( &p, cg_, iconsRect() );
01293     drawPreview( &p, cg_, previewRect() );
01294 
01295     painter->drawPixmap( 0, 0, buf );
01296 }
01297 
01298 void LayerItem::setup()
01299 {
01300     super::setup();
01301     setHeight( listView()->d->itemHeight );
01302 }
01303 
01304 void LayerItem::setSelected( bool selected )
01305 {
01306     if( !selected && ( isActive() || this == listView()->currentItem() ) )
01307         return;
01308     super::setSelected( selected );
01309 }
01310 
01311 
01313 // Convenience Methods //
01315 
01316 LayerItem *LayerList::firstChild() const { return static_cast<LayerItem*>( super::firstChild() ); }
01317 LayerItem *LayerList::lastChild() const { return static_cast<LayerItem*>( super::lastChild() ); }
01318 LayerList *LayerItem::listView() const { return static_cast<LayerList*>( super::listView() ); }
01319 void LayerItem::update() const { listView()->repaintItem( this ); }
01320 LayerItem *LayerItem::firstChild() const { return static_cast<LayerItem*>( super::firstChild() ); }
01321 LayerItem *LayerItem::nextSibling() const { return static_cast<LayerItem*>( super::nextSibling() ); }
01322 LayerItem *LayerItem::parent() const { return static_cast<LayerItem*>( super::parent() ); }
01323 
01324 
01325 #include "layerlist.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys