libkonq Library API Documentation

konq_iconviewwidget.cc

00001 /* This file is part of the KDE projects
00002    Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
00003    Copyright (C) 2000, 2001, 2002 David Faure <david@mandrakesoft.com>
00004 
00005    This program is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013     General Public License for more details.
00014 
00015    You should have received a copy of the GNU General Public License
00016    along with this program; see the file COPYING.  If not, write to
00017    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00018    Boston, MA 02111-1307, USA.
00019 */
00020 #include "konq_iconviewwidget.h"
00021 #include "konq_undo.h"
00022 #include "konq_sound.h"
00023 
00024 #include <qclipboard.h>
00025 #include <qlayout.h>
00026 #include <qtimer.h>
00027 #include <qpainter.h>
00028 #include <qtooltip.h>
00029 #include <qlabel.h>
00030 #include <qmovie.h>
00031 #include <qregexp.h>
00032 
00033 #include <kapplication.h>
00034 #include <kdebug.h>
00035 #include <kio/previewjob.h>
00036 #include <kfileivi.h>
00037 #include <konq_settings.h>
00038 #include <konq_drag.h>
00039 #include <konq_operations.h>
00040 #include <kglobalsettings.h>
00041 #include <kpropertiesdialog.h>
00042 #include <kipc.h>
00043 #include <kicontheme.h>
00044 #include <kiconeffect.h>
00045 #include <kurldrag.h>
00046 #include <kstandarddirs.h>
00047 #include <kprotocolinfo.h>
00048 
00049 #include <assert.h>
00050 #include <unistd.h>
00051 
00052 class KFileTip: public QFrame
00053 {
00054 public:
00055     KFileTip( KonqIconViewWidget* parent ) : QFrame( 0, 0, WStyle_Customize | WStyle_NoBorder | WStyle_Tool | WStyle_StaysOnTop | WX11BypassWM ),
00056 
00057           m_corner( 0 ),
00058           m_filter( false ),
00059           m_view( parent ),
00060           m_item( 0 ),
00061           m_previewJob( 0 ),
00062           m_ivi( 0 )
00063     {
00064         m_iconLabel = new QLabel(this);
00065         m_textLabel = new QLabel(this);
00066         m_textLabel->setAlignment(Qt::AlignAuto | Qt::AlignTop);
00067 
00068         QGridLayout* layout = new QGridLayout(this, 1, 2, 8, 0);
00069         layout->addWidget(m_iconLabel, 0, 0);
00070         layout->addWidget(m_textLabel, 0, 1);
00071         layout->setResizeMode(QLayout::Fixed);
00072 
00073         setPalette( QToolTip::palette() );
00074         setMargin( 1 );
00075         setFrameStyle( QFrame::Plain | QFrame::Box );
00076 
00077         hide();
00078     }
00079 
00080     void setOptions( bool on, bool preview, int num)
00081     {
00082         m_num = num;
00083         m_preview = preview;
00084         m_on = on;
00085     }
00086 
00087     void setItem( KFileIVI *ivi );
00088 
00089     virtual bool eventFilter( QObject *, QEvent *e );
00090 
00091     void gotPreview( const KFileItem*, const QPixmap& );
00092     void gotPreviewResult();
00093 
00094 protected:
00095     virtual void drawContents( QPainter *p );
00096     virtual void timerEvent( QTimerEvent * );
00097     virtual void resizeEvent( QResizeEvent * );
00098 
00099 private:
00100     void setFilter( bool enable );
00101 
00102     void reposition();
00103 
00104     QLabel*    m_iconLabel;
00105     QLabel*    m_textLabel;
00106     int        m_num;
00107     bool       m_on;
00108     bool       m_preview;
00109     QPixmap    m_corners[4];
00110     int        m_corner;
00111     bool       m_filter;
00112     KonqIconViewWidget*       m_view;
00113     KFileItem* m_item;
00114     KIO::PreviewJob* m_previewJob;
00115     KFileIVI*  m_ivi;
00116 };
00117 
00118 void KFileTip::setItem( KFileIVI *ivi )
00119 {
00120     if (!m_on) return;
00121     if (m_ivi == ivi) return;
00122 
00123     if ( m_previewJob ) {
00124         m_previewJob->kill();
00125         m_previewJob = 0;
00126     }
00127 
00128     m_ivi = ivi;
00129     m_item = ivi ? ivi->item() : 0;
00130 
00131     QString text = ivi ? ivi->item()->getToolTipText( m_num ) : QString::null;
00132     if ( !text.isEmpty() ) {
00133         hide();
00134         m_textLabel -> setText( text );
00135 
00136         killTimers();
00137         setFilter( true );
00138 
00139         if (m_preview) {
00140             m_iconLabel -> setPixmap(*(ivi->pixmap()));
00141             KFileItemList oneItem;
00142             oneItem.append( ivi->item() );
00143 
00144             m_previewJob = KIO::filePreview( oneItem, 256, 256, 64, 70, true, true, 0);
00145             connect( m_previewJob, SIGNAL( gotPreview( const KFileItem *, const QPixmap & ) ),
00146                     m_view, SLOT( slotToolTipPreview( const KFileItem *, const QPixmap & ) ) );
00147             connect( m_previewJob, SIGNAL( result( KIO::Job * ) ),
00148                     m_view, SLOT( slotToolTipPreviewResult() ) );
00149         }
00150 
00151         startTimer( 700 );
00152     }
00153     else {
00154         killTimers();
00155         if ( isVisible() ) {
00156             setFilter( false );
00157             hide();
00158         }
00159     }
00160 }
00161 
00162 void KFileTip::reposition()
00163 {
00164     if (!m_ivi) return;
00165 
00166     QRect rect = m_ivi->rect();
00167     QPoint off = m_view->mapToGlobal( m_view->contentsToViewport( QPoint( 0, 0 ) ) );
00168     rect.moveBy( off.x(), off.y() );
00169 
00170     QPoint pos = rect.center();
00171     // m_corner:
00172     // 0: upperleft
00173     // 1: upperright
00174     // 2: lowerleft
00175     // 3: lowerright
00176     // 4+: none
00177     m_corner = 0;
00178     // should the tooltip be shown to the left or to the right of the ivi ?
00179     QRect desk = QApplication::desktop()->screenGeometry(QApplication::desktop()->screenNumber(rect.center()));
00180     if (rect.center().x() + width() > desk.right())
00181     {
00182         // to the left
00183         if (pos.x() - width() < 0) {
00184             pos.setX(0);
00185             m_corner = 4;
00186         } else {
00187             pos.setX( pos.x() - width() );
00188             m_corner = 1;
00189         }
00190     }
00191     // should the tooltip be shown above or below the ivi ?
00192     if (rect.bottom() + height() > desk.bottom())
00193     {
00194         // above
00195         pos.setY( rect.top() - height() );
00196         m_corner += 2;
00197     }
00198     else pos.setY( rect.bottom() );
00199 
00200     move( pos );
00201     update();
00202 }
00203 
00204 void KFileTip::gotPreview( const KFileItem* item, const QPixmap& pixmap )
00205 {
00206     m_previewJob = 0;
00207     if (item != m_item) return;
00208 
00209     m_iconLabel -> setPixmap(pixmap);
00210 }
00211 
00212 void KFileTip::gotPreviewResult()
00213 {
00214     m_previewJob = 0;
00215 }
00216 
00217 void KFileTip::drawContents( QPainter *p )
00218 {
00219     static const char * const names[] = {
00220         "arrow_topleft",
00221         "arrow_topright",
00222         "arrow_bottomleft",
00223         "arrow_bottomright"
00224     };
00225 
00226     if (m_corner >= 4) {  // 4 is empty, so don't draw anything
00227         QFrame::drawContents( p );
00228         return;
00229     }
00230 
00231     if ( m_corners[m_corner].isNull())
00232         m_corners[m_corner].load( locate( "data", QString::fromLatin1( "konqueror/pics/%1.png" ).arg( names[m_corner] ) ) );
00233 
00234     QPixmap &pix = m_corners[m_corner];
00235 
00236     switch ( m_corner )
00237     {
00238         case 0:
00239             p->drawPixmap( 3, 3, pix );
00240             break;
00241         case 1:
00242             p->drawPixmap( width() - pix.width() - 3, 3, pix );
00243             break;
00244         case 2:
00245             p->drawPixmap( 3, height() - pix.height() - 3, pix );
00246             break;
00247         case 3:
00248             p->drawPixmap( width() - pix.width() - 3, height() - pix.height() - 3, pix );
00249             break;
00250     }
00251 
00252     QFrame::drawContents( p );
00253 }
00254 
00255 void KFileTip::setFilter( bool enable )
00256 {
00257     if ( enable == m_filter ) return;
00258 
00259     if ( enable ) {
00260         kapp->installEventFilter( this );
00261         QApplication::setGlobalMouseTracking( true );
00262     }
00263     else {
00264         QApplication::setGlobalMouseTracking( false );
00265         kapp->removeEventFilter( this );
00266     }
00267     m_filter = enable;
00268 }
00269 
00270 void KFileTip::timerEvent( QTimerEvent * )
00271 {
00272     killTimers();
00273     if ( !isVisible() ) {
00274         startTimer( 15000 );
00275         reposition();
00276         show();
00277     }
00278     else {
00279         setFilter( false );
00280         hide();
00281     }
00282 }
00283 
00284 void KFileTip::resizeEvent( QResizeEvent* event )
00285 {
00286     QFrame::resizeEvent(event);
00287     reposition();
00288 }
00289 
00290 bool KFileTip::eventFilter( QObject *, QEvent *e )
00291 {
00292     switch ( e->type() )
00293     {
00294         case QEvent::Leave:
00295         case QEvent::MouseButtonPress:
00296         case QEvent::MouseButtonRelease:
00297         case QEvent::KeyPress:
00298         case QEvent::KeyRelease:
00299         case QEvent::FocusIn:
00300         case QEvent::FocusOut:
00301         case QEvent::Wheel:
00302             killTimers();
00303             setFilter( false );
00304             hide();
00305         default: break;
00306     }
00307 
00308     return false;
00309 }
00310 
00311 struct KonqIconViewWidgetPrivate
00312 {
00313     KonqIconViewWidgetPrivate() {
00314         pActiveItem = 0;
00315         bSoundPreviews = false;
00316         pSoundItem = 0;
00317         bSoundItemClicked = false;
00318         pSoundPlayer = 0;
00319         pSoundTimer = 0;
00320         pPreviewJob = 0;
00321         bAllowSetWallpaper = false;
00322         updateAfterPreview = false;
00323         gridXspacing = 50;
00324 
00325         doAnimations = true;
00326         m_movie = 0L;
00327         m_movieBlocked = 0;
00328         pFileTip = 0;
00329     }
00330     ~KonqIconViewWidgetPrivate() {
00331         delete pSoundPlayer;
00332         delete pSoundTimer;
00333         delete m_movie;
00334         delete pFileTip;
00335         //delete pPreviewJob; done by stopImagePreview
00336     }
00337     KFileIVI *pActiveItem;
00338     // Sound preview
00339     KFileIVI *pSoundItem;
00340     KonqSoundPlayer *pSoundPlayer;
00341     QTimer *pSoundTimer;
00342     bool bSoundPreviews;
00343     bool bSoundItemClicked;
00344     bool bAllowSetWallpaper;
00345     bool updateAfterPreview;
00346     int gridXspacing;
00347 
00348     // Animated icons support
00349     bool doAnimations;
00350     QMovie* m_movie;
00351     int m_movieBlocked;
00352     QString movieFileName;
00353 
00354     KIO::PreviewJob *pPreviewJob;
00355     KFileTip* pFileTip;
00356     QStringList previewSettings;
00357 };
00358 
00359 KonqIconViewWidget::KonqIconViewWidget( QWidget * parent, const char * name, WFlags f, bool kdesktop )
00360     : KIconView( parent, name, f ),
00361       m_rootItem( 0L ), m_size( 0 ) /* default is DesktopIcon size */,
00362       m_bDesktop( kdesktop ),
00363       m_bSetGridX( !kdesktop ) /* No line breaking on the desktop */
00364 {
00365     d = new KonqIconViewWidgetPrivate;
00366     connect( this, SIGNAL( dropped( QDropEvent *, const QValueList<QIconDragItem> & ) ),
00367              this, SLOT( slotDropped( QDropEvent*, const QValueList<QIconDragItem> & ) ) );
00368 
00369     connect( this, SIGNAL( selectionChanged() ),
00370              this, SLOT( slotSelectionChanged() ) );
00371 
00372     kapp->addKipcEventMask( KIPC::IconChanged );
00373     connect( kapp, SIGNAL(iconChanged(int)), SLOT(slotIconChanged(int)) );
00374     connect( this, SIGNAL(onItem(QIconViewItem *)), SLOT(slotOnItem(QIconViewItem *)) );
00375     connect( this, SIGNAL(onViewport()), SLOT(slotOnViewport()) );
00376     connect( this, SIGNAL(itemRenamed(QIconViewItem *, const QString &)), SLOT(slotItemRenamed(QIconViewItem *, const QString &)) );
00377 
00378     // hardcoded settings
00379     setSelectionMode( QIconView::Extended );
00380     setItemTextPos( QIconView::Bottom );
00381 
00382     d->pFileTip = new KFileTip(this);
00383 
00384     calculateGridX();
00385     setAutoArrange( true );
00386     setSorting( true, sortDirection() );
00387     readAnimatedIconsConfig();
00388     m_bSortDirsFirst = true;
00389     m_bMousePressed = false;
00390     m_LineupMode = LineupBoth;
00391     // emit our signals
00392     slotSelectionChanged();
00393     m_iconPositionGroupPrefix = QString::fromLatin1( "IconPosition::" );
00394     KonqUndoManager::incRef();
00395 }
00396 
00397 KonqIconViewWidget::~KonqIconViewWidget()
00398 {
00399     stopImagePreview();
00400     KonqUndoManager::decRef();
00401     delete d;
00402 }
00403 
00404 bool KonqIconViewWidget::maySetWallpaper()
00405 {
00406     return d->bAllowSetWallpaper;
00407 }
00408 
00409 void KonqIconViewWidget::setMaySetWallpaper(bool b)
00410 {
00411     d->bAllowSetWallpaper = b;
00412 }
00413 
00414 void KonqIconViewWidget::focusOutEvent( QFocusEvent * ev )
00415 {
00416     // We can't possibly have the mouse pressed and still lose focus.
00417     // Well, we can, but when we regain focus we should assume the mouse is
00418     // not down anymore or the slotOnItem code will break with highlighting!
00419     m_bMousePressed = false;
00420     KIconView::focusOutEvent( ev );
00421 }
00422 
00423 void KonqIconViewWidget::slotItemRenamed(QIconViewItem *item, const QString &name)
00424 {
00425     kdDebug(1203) << "KonqIconViewWidget::slotItemRenamed" << endl;
00426     KFileIVI *viewItem = static_cast<KFileIVI *>(item);
00427     KFileItem *fileItem = viewItem->item();
00428     
00429     // The correct behavior is to show the old name until the rename has successfully
00430     // completed. Unfortunately, KIconView forces us to allow the text to be changed
00431     // before we try the rename, so set it back to the pre-rename state.
00432     viewItem->setText( fileItem->text() );
00433 
00434     // Don't do anything if the user renamed to a blank name.
00435     if( !name.isEmpty() )
00436     {
00437         // Actually attempt the rename. If it succeeds, KDirLister will update the name.
00438         KonqOperations::rename( this, fileItem->url(), KIO::encodeFileName( name ) );
00439     }
00440 }
00441 
00442 void KonqIconViewWidget::slotIconChanged( int group )
00443 {
00444     if (group != KIcon::Desktop)
00445         return;
00446 
00447     int size = m_size;
00448     if ( m_size == 0 )
00449       m_size = -1; // little trick to force grid change in setIcons
00450     setIcons( size ); // force re-determining all icons
00451     readAnimatedIconsConfig();
00452 }
00453 
00454 void KonqIconViewWidget::readAnimatedIconsConfig()
00455 {
00456     KConfigGroup cfgGroup( KGlobal::config(), "DesktopIcons" );
00457     d->doAnimations = cfgGroup.readBoolEntry( "Animated", true /*default*/ );
00458     d->gridXspacing = cfgGroup.readNumEntry( "GridXSpacing", 50);
00459 }
00460 
00461 void KonqIconViewWidget::slotOnItem( QIconViewItem *item )
00462 {
00463     // Reset icon of previous item
00464     if( d->pActiveItem != 0L && d->pActiveItem != item )
00465     {
00466         if ( d->m_movie && d->pActiveItem->isAnimated() )
00467         {
00468             d->m_movie->pause(); // we'll see below what we do with it
00469             d->pActiveItem->setAnimated( false );
00470             d->pActiveItem->refreshIcon( true );
00471         }
00472         else {
00473             d->pActiveItem->setActive( false );
00474         }
00475         d->pActiveItem = 0L;
00476         d->pFileTip->setItem( 0L );
00477     }
00478 
00479     // Stop sound
00480     if (d->pSoundPlayer != 0 && static_cast<KFileIVI *>(item) != d->pSoundItem)
00481     {
00482         d->pSoundPlayer->stop();
00483 
00484         d->pSoundItem = 0;
00485         if (d->pSoundTimer && d->pSoundTimer->isActive())
00486             d->pSoundTimer->stop();
00487     }
00488 
00489     if ( !m_bMousePressed )
00490     {
00491         if( item != d->pActiveItem )
00492         {
00493             d->pActiveItem = static_cast<KFileIVI *>(item);
00494             if ( topLevelWidget() == kapp->activeWindow() )
00495                 d->pFileTip->setItem( d->pActiveItem );
00496 
00497             if ( d->doAnimations && d->pActiveItem && d->pActiveItem->hasAnimation() )
00498             {
00499                 //kdDebug(1203) << "Playing animation for: " << d->pActiveItem->mouseOverAnimation() << endl;
00500                 // Check if cached movie can be used
00501 #if 0 // Qt-mng bug, reusing the movie doesn't work currently.
00502                 if ( d->m_movie && d->movieFileName == d->pActiveItem->mouseOverAnimation() )
00503                 {
00504                     d->pActiveItem->setAnimated( true );
00505                     if (d->m_movieBlocked) {
00506                         kdDebug(1203) << "onitem, but blocked" << endl;
00507                         d->m_movie->pause();
00508                     }
00509                     else {
00510                         kdDebug(1203) << "we go ahead.." << endl;
00511                         d->m_movieBlocked++;
00512                         QTimer::singleShot(300, this, SLOT(slotReenableAnimation()));
00513                         d->m_movie->restart();
00514                         d->m_movie->unpause();
00515                     }
00516                 }
00517                 else
00518 #endif
00519                 {
00520                     QMovie movie = KGlobal::iconLoader()->loadMovie( d->pActiveItem->mouseOverAnimation(), KIcon::Desktop, d->pActiveItem->iconSize() );
00521                     if ( !movie.isNull() )
00522                     {
00523                         delete d->m_movie;
00524                         d->m_movie = new QMovie( movie ); // shallow copy, don't worry
00525                         // Fix alpha-channel - currently only if no background pixmap,
00526                         // the bg pixmap case requires to uncomment the code at qmovie.cpp:404
00527                         const QPixmap* pm = backgroundPixmap();
00528                         bool hasPixmap = pm && !pm->isNull();
00529                         if ( !hasPixmap ) {
00530                             pm = viewport()->backgroundPixmap();
00531                             hasPixmap = pm && !pm->isNull();
00532                         }
00533                         if (!hasPixmap && backgroundMode() != NoBackground)
00534                            d->m_movie->setBackgroundColor( viewport()->backgroundColor() );
00535                         d->m_movie->connectUpdate( this, SLOT( slotMovieUpdate(const QRect &) ) );
00536                         d->m_movie->connectStatus( this, SLOT( slotMovieStatus(int) ) );
00537                         d->movieFileName = d->pActiveItem->mouseOverAnimation();
00538                         d->pActiveItem->setAnimated( true );
00539                     }
00540                     else
00541                     {
00542                         d->pActiveItem->setAnimated( false );
00543                         if (d->m_movie)
00544                             d->m_movie->pause();
00545                         // No movie available, remember it
00546                         d->pActiveItem->setMouseOverAnimation( QString::null );
00547                     }
00548                 }
00549             } // animations
00550             // Only do the normal "mouseover" effect if no animation is in use
00551             if (!d->pActiveItem->isAnimated())
00552             {
00553                 d->pActiveItem->setActive( true );
00554             }
00555         }
00556         else // No change in current item
00557         {
00558             // No effect. If we want to underline on hover, we should
00559             // force the IVI to repaint here, though!
00560             d->pActiveItem = 0L;
00561             d->pFileTip->setItem( 0L );
00562         }
00563     } // bMousePressed
00564     else
00565     {
00566         // All features disabled during mouse clicking, e.g. rectangular
00567         // selection
00568         d->pActiveItem = 0L;
00569         d->pFileTip->setItem( 0L );
00570     }
00571 
00572     // ## shouldn't this be disabled during rectangular selection too ?
00573     if (d->bSoundPreviews && d->pSoundPlayer &&
00574         d->pSoundPlayer->mimeTypes().contains(
00575             static_cast<KFileIVI *>(item)->item()->mimetype()))
00576     {
00577         d->pSoundItem = static_cast<KFileIVI *>(item);
00578         d->bSoundItemClicked = false;
00579         if (!d->pSoundTimer)
00580         {
00581             d->pSoundTimer = new QTimer(this);
00582             connect(d->pSoundTimer, SIGNAL(timeout()), SLOT(slotStartSoundPreview()));
00583         }
00584         if (d->pSoundTimer->isActive())
00585             d->pSoundTimer->stop();
00586         d->pSoundTimer->start(500, true);
00587     }
00588     else
00589     {
00590         if (d->pSoundPlayer)
00591             d->pSoundPlayer->stop();
00592         d->pSoundItem = 0;
00593         if (d->pSoundTimer && d->pSoundTimer->isActive())
00594             d->pSoundTimer->stop();
00595     }
00596 }
00597 
00598 void KonqIconViewWidget::slotOnViewport()
00599 {
00600     d->pFileTip->setItem( 0L );
00601 
00602     if (d->pSoundPlayer)
00603       d->pSoundPlayer->stop();
00604     d->pSoundItem = 0;
00605     if (d->pSoundTimer && d->pSoundTimer->isActive())
00606       d->pSoundTimer->stop();
00607 
00608     if (d->pActiveItem == 0L)
00609         return;
00610 
00611     if ( d->doAnimations && d->m_movie && d->pActiveItem->isAnimated() )
00612     {
00613         d->pActiveItem->setAnimated( false );
00614 #if 0
00615         // Aborting before the end of the animation ?
00616         if (d->m_movie->running()) {
00617             d->m_movie->pause();
00618             d->m_movieBlocked++;
00619             kdDebug(1203) << "on viewport, blocking" << endl;
00620             QTimer::singleShot(300, this, SLOT(slotReenableAnimation()));
00621         }
00622 #endif
00623         d->pActiveItem->refreshIcon( true );
00624         Q_ASSERT( d->pActiveItem->state() == KIcon::DefaultState );
00625         //delete d->m_movie;
00626         //d->m_movie = 0L;
00627         // TODO a timer to delete the movie after some time if unused?
00628     }
00629     else
00630     {
00631         d->pActiveItem->setActive( false );
00632     }
00633     d->pActiveItem = 0L;
00634 }
00635 
00636 void KonqIconViewWidget::slotStartSoundPreview()
00637 {
00638   if (!d->pSoundItem || d->bSoundItemClicked)
00639     return;
00640 
00641   d->pSoundPlayer->play(d->pSoundItem->item()->url().url());
00642 }
00643 
00644 void KonqIconViewWidget::slotPreview(const KFileItem *item, const QPixmap &pix)
00645 {
00646     // ### slow. Idea: move KonqKfmIconView's m_itemDict into this class
00647     for (QIconViewItem *it = firstItem(); it; it = it->nextItem())
00648     {
00649         if (static_cast<KFileIVI *>(it)->item() == item)
00650             static_cast<KFileIVI *>(it)->setThumbnailPixmap(pix);
00651         {
00652     }
00653             d->updateAfterPreview = true;
00654         }
00655 }
00656 
00657 void KonqIconViewWidget::slotPreviewResult()
00658 {
00659     d->pPreviewJob = 0;
00660     emit imagePreviewFinished();
00661     if (autoArrange() && d->updateAfterPreview ) {
00662         arrangeItemsInGrid();
00663         d->updateAfterPreview = false;
00664     }
00665 }
00666 
00667 void KonqIconViewWidget::slotToolTipPreview(const KFileItem* item, const QPixmap &pix)
00668 {
00669     if (d->pFileTip) d->pFileTip->gotPreview( item, pix );
00670 }
00671 
00672 void KonqIconViewWidget::slotToolTipPreviewResult()
00673 {
00674     if (d->pFileTip) d->pFileTip->gotPreviewResult();
00675 }
00676 
00677 void KonqIconViewWidget::slotMovieUpdate( const QRect& rect )
00678 {
00679     //kdDebug(1203) << "KonqIconViewWidget::slotMovieUpdate " << rect.x() << "," << rect.y() << " " << rect.width() << "x" << rect.height() << endl;
00680     Q_ASSERT( d );
00681     Q_ASSERT( d->m_movie );
00682     // seems stopAnimation triggers one last update
00683     if ( d->pActiveItem && d->m_movie && d->pActiveItem->isAnimated() ) {
00684         const QPixmap &frame = d->m_movie->framePixmap();
00685         // This can happen if the icon was scaled to the desired size, so KIconLoader
00686         // will happily return a movie with different dimensions than the icon
00687         int iconSize=d->pActiveItem->iconSize();
00688         if (iconSize==0) iconSize = KGlobal::iconLoader()->currentSize( KIcon::Desktop );
00689         if ( frame.width() != iconSize || frame.height() != iconSize ) {
00690             d->pActiveItem->setAnimated( false );
00691             d->m_movie->pause();
00692             // No movie available, remember it
00693             d->pActiveItem->setMouseOverAnimation( QString::null );
00694             d->pActiveItem->setActive( true );
00695             return;
00696         }
00697         d->pActiveItem->setPixmapDirect( frame, false, false /*no redraw*/ );
00698         QRect pixRect = d->pActiveItem->pixmapRect(false);
00699         repaintContents( pixRect.x() + rect.x(), pixRect.y() + rect.y(), rect.width(), rect.height(), false );
00700     }
00701 }
00702 
00703 void KonqIconViewWidget::slotMovieStatus( int status )
00704 {
00705     if ( status < 0 ) {
00706         // Error playing the MNG -> forget about it and do normal iconeffect
00707         if ( d->pActiveItem && d->pActiveItem->isAnimated() ) {
00708             d->pActiveItem->setAnimated( false );
00709             d->pActiveItem->setMouseOverAnimation( QString::null );
00710             d->pActiveItem->setActive( true );
00711         }
00712     }
00713 }
00714 
00715 void KonqIconViewWidget::slotReenableAnimation()
00716 {
00717     if (!--d->m_movieBlocked) {
00718         if ( d->pActiveItem && d->m_movie && d->m_movie->paused()) {
00719             kdDebug(1203) << "reenabled animation" << endl;
00720             d->m_movie->restart();
00721             d->m_movie->unpause();
00722         }
00723     }
00724 }
00725 
00726 void KonqIconViewWidget::clear()
00727 {
00728     d->pFileTip->setItem( 0L );
00729     stopImagePreview(); // Just in case
00730     KIconView::clear();
00731     d->pActiveItem = 0L;
00732 }
00733 
00734 void KonqIconViewWidget::takeItem( QIconViewItem *item )
00735 {
00736     if ( d->pActiveItem == static_cast<KFileIVI *>(item) )
00737     {
00738         d->pFileTip->setItem( 0L );
00739         d->pActiveItem = 0L;
00740     }
00741 
00742     if ( d->pPreviewJob )
00743       d->pPreviewJob->removeItem( static_cast<KFileIVI *>(item)->item() );
00744 
00745     KIconView::takeItem( item );
00746 }
00747 
00748 void KonqIconViewWidget::setThumbnailPixmap( KFileIVI * item, const QPixmap & pixmap )
00749 {
00750     if ( item )
00751     {
00752         if ( d->pActiveItem == item )
00753         {
00754             d->pFileTip->setItem( 0L );
00755             d->pActiveItem = 0L;
00756         }
00757         item->setThumbnailPixmap( pixmap );
00758         if ( m_bSetGridX &&  item->width() > gridX() )
00759         {
00760           setGridX( item->width() );
00761           if (autoArrange())
00762             arrangeItemsInGrid();
00763         }
00764     }
00765 }
00766 
00767 bool KonqIconViewWidget::initConfig( bool bInit )
00768 {
00769     bool fontChanged = false;
00770     m_pSettings = KonqFMSettings::settings();
00771 
00772     // Color settings
00773     QColor normalTextColor       = m_pSettings->normalTextColor();
00774     setItemColor( normalTextColor );
00775 
00776     if (m_bDesktop)
00777     {
00778       QColor itemTextBg = m_pSettings->itemTextBackground();
00779       if ( itemTextBg.isValid() )
00780           setItemTextBackground( itemTextBg );
00781       else
00782           setItemTextBackground( NoBrush );
00783     }
00784 
00785 
00786     d->pFileTip->setOptions(m_pSettings->showFileTips() && QToolTip::isGloballyEnabled(),
00787                             m_pSettings->showPreviewsInFileTips(),
00788                             m_pSettings->numFileTips());
00789 
00790     // Font settings
00791     QFont font( m_pSettings->standardFont() );
00792     font.setUnderline( m_pSettings->underlineLink() );
00793     if ( font != KonqIconViewWidget::font() )
00794     {
00795         setFont( font );
00796         if (!bInit)
00797         {
00798             // QIconView doesn't do it by default... but if the font is made much
00799             // bigger, we really need to give more space between the icons
00800             fontChanged = true;
00801         }
00802     }
00803     setWordWrapIconText( m_pSettings->wordWrapText() );
00804 
00805     if (!bInit)
00806         updateContents();
00807     return fontChanged;
00808 }
00809 
00810 void KonqIconViewWidget::disableSoundPreviews()
00811 {
00812     d->bSoundPreviews = false;
00813 
00814     if (d->pSoundPlayer)
00815       d->pSoundPlayer->stop();
00816     d->pSoundItem = 0;
00817     if (d->pSoundTimer && d->pSoundTimer->isActive())
00818       d->pSoundTimer->stop();
00819 }
00820 
00821 void KonqIconViewWidget::setIcons( int size, const QStringList& stopImagePreviewFor )
00822 {
00823     //kdDebug(1203) << "KonqIconViewWidget::setIcons( " << size << " , " << stopImagePreviewFor.join(",") << ")" << endl;
00824     bool sizeChanged = (m_size != size);
00825     int oldGridX = gridX();
00826     m_size = size;
00827 
00828     if ( sizeChanged )
00829     {
00830         setSpacing( (size > KIcon::SizeSmall) ? 5 : 0 );
00831     }
00832 
00833     if ( sizeChanged || !stopImagePreviewFor.isEmpty() )
00834     {
00835         calculateGridX();
00836     }
00837     bool stopAll = !stopImagePreviewFor.isEmpty() && stopImagePreviewFor.first() == "*";
00838     // Do this even if size didn't change, since this is used by refreshMimeTypes...
00839     for ( QIconViewItem *it = firstItem(); it; it = it->nextItem() ) {
00840         KFileIVI * ivi = static_cast<KFileIVI *>( it );
00841         // Set a normal icon for files that are not thumbnails, and for files
00842         // that are thumbnails but for which it should be stopped
00843         if ( !ivi->isThumbnail() ||
00844              stopAll ||
00845              mimeTypeMatch( ivi->item()->mimetype(), stopImagePreviewFor ) )
00846         {
00847             ivi->setIcon( size, ivi->state(), true, false );
00848         }
00849         else
00850             ivi->invalidateThumb( ivi->state(), false );
00851     }
00852 
00853     if ( autoArrange() && (oldGridX != gridX() || !stopImagePreviewFor.isEmpty()) )
00854         arrangeItemsInGrid( true ); // take new grid into account and repaint
00855     else
00856         viewport()->update(); //Repaint later..
00857 }
00858 
00859 bool KonqIconViewWidget::mimeTypeMatch( const QString& mimeType, const QStringList& mimeList ) const
00860 {
00861     for (QStringList::ConstIterator mt = mimeList.begin(); mt != mimeList.end(); ++mt)
00862     {
00863         if ( mimeType == *mt )
00864             return true;
00865         // Support for *mt == "image/*"
00866         QString tmp( mimeType );
00867         if ( (*mt).endsWith("*") && tmp.replace(QRegExp("/.*"), "/*") == (*mt) )
00868             return true;
00869     }
00870     return false;
00871 }
00872 
00873 void KonqIconViewWidget::setItemTextPos( ItemTextPos pos )
00874 {
00875     if ( m_bSetGridX )
00876     {
00877         calculateGridX();
00878         if ( itemTextPos() != pos )
00879         {
00880             if ( pos == QIconView::Right )
00881                 setGridX( gridX() + 100 );
00882             else
00883                 setGridX( gridX() - 100 );
00884         }
00885     }
00886 
00887     KIconView::setItemTextPos( pos );
00888 }
00889 
00890 void KonqIconViewWidget::calculateGridX()
00891 {
00892     if ( m_bSetGridX )
00893         setGridX( gridXValue() );
00894 }
00895 
00896 int KonqIconViewWidget::gridXValue() const
00897 {
00898     int sz = m_size ? m_size : KGlobal::iconLoader()->currentSize( KIcon::Desktop );
00899     int newGridX = sz + (!m_bSetGridX ? d->gridXspacing : 50) + (( itemTextPos() == QIconView::Right ) ? 100 : 0);
00900     //kdDebug(1203) << "gridXValue: " << newGridX << " sz=" << sz << endl;
00901     return newGridX;
00902 }
00903 
00904 void KonqIconViewWidget::refreshMimeTypes()
00905 {
00906     for ( QIconViewItem *it = firstItem(); it; it = it->nextItem() )
00907         (static_cast<KFileIVI *>( it ))->item()->refreshMimeType();
00908     setIcons( m_size );
00909 }
00910 
00911 void KonqIconViewWidget::setURL( const KURL &kurl )
00912 {
00913     stopImagePreview();
00914     m_url = kurl;
00915     if ( m_url.isLocalFile() )
00916         m_dotDirectoryPath = m_url.path(1).append( ".directory" );
00917     else
00918         m_dotDirectoryPath = QString::null;
00919 }
00920 
00921 void KonqIconViewWidget::startImagePreview( const QStringList &, bool force )
00922 {
00923     stopImagePreview(); // just in case
00924 
00925     // Check config
00926     KConfigGroup group( KGlobal::config(), "PreviewSettings" );
00927     if ( !group.readBoolEntry( url().protocol(), true ) ) {
00928         kdDebug(1203) << "Previews disabled for protocol " << url().protocol() << endl;
00929         emit imagePreviewFinished();
00930         return;
00931     }
00932 
00933     if ((d->bSoundPreviews = d->previewSettings.contains( "audio/" )) &&
00934         !d->pSoundPlayer)
00935     {
00936       KLibFactory *factory = KLibLoader::self()->factory("konq_sound");
00937       if (factory)
00938         d->pSoundPlayer = static_cast<KonqSoundPlayer *>(
00939           factory->create(this, 0, "KonqSoundPlayer"));
00940       d->bSoundPreviews = (d->pSoundPlayer != 0L);
00941     }
00942 
00943     bool onlyAudio = true;
00944     KFileItemList items;
00945     for ( QIconViewItem *it = firstItem(); it; it = it->nextItem() )
00946         if ( force || !static_cast<KFileIVI *>( it )->hasValidThumbnail() )
00947             items.append( static_cast<KFileIVI *>( it )->item() );
00948 
00949     for ( QStringList::ConstIterator it = d->previewSettings.begin(); it != d->previewSettings.end(); ++it ) {
00950         if ( (*it).startsWith( "audio/" ) )
00951             d->bSoundPreviews = true;
00952         else
00953             onlyAudio = false;
00954     }
00955 
00956     if ( items.isEmpty() || onlyAudio ) {
00957         emit imagePreviewFinished();
00958         return; // don't start the preview job if not really necessary
00959     }
00960 
00961     int iconSize = m_size ? m_size : KGlobal::iconLoader()->currentSize( KIcon::Desktop );
00962     int size;
00963 
00964     if ( group.readBoolEntry("BoostSize", false) ) {
00965         if (iconSize < 28)
00966             size = 48;
00967         else if (iconSize < 40)
00968 #if 0 // TMS sizes, enable when KIO::Previewjob uses them
00969             size = 64;
00970         else if (iconSize < 60)
00971             size = 96;
00972         else
00973             size = 128;
00974 #endif
00975             size = 60;
00976         else size = 90;
00977     } else {
00978         size = iconSize;
00979         iconSize /= 2;
00980     }
00981 
00982     d->pPreviewJob = KIO::filePreview( items, size, size, iconSize,
00983         m_pSettings->textPreviewIconTransparency(), true /* scale */,
00984         true /* save */, &(d->previewSettings) );
00985     connect( d->pPreviewJob, SIGNAL( gotPreview( const KFileItem *, const QPixmap & ) ),
00986              this, SLOT( slotPreview( const KFileItem *, const QPixmap & ) ) );
00987     connect( d->pPreviewJob, SIGNAL( result( KIO::Job * ) ),
00988              this, SLOT( slotPreviewResult() ) );
00989 }
00990 
00991 void KonqIconViewWidget::stopImagePreview()
00992 {
00993     if (d->pPreviewJob)
00994     {
00995         d->pPreviewJob->kill();
00996         d->pPreviewJob = 0;
00997         if (autoArrange())
00998             arrangeItemsInGrid();
00999     }
01000 }
01001 
01002 bool KonqIconViewWidget::isPreviewRunning() const
01003 {
01004     return d->pPreviewJob;
01005 }
01006 
01007 KFileItemList KonqIconViewWidget::selectedFileItems()
01008 {
01009     KFileItemList lstItems;
01010 
01011     QIconViewItem *it = firstItem();
01012     for (; it; it = it->nextItem() )
01013         if ( it->isSelected() ) {
01014             KFileItem *fItem = (static_cast<KFileIVI *>(it))->item();
01015             lstItems.append( fItem );
01016         }
01017     return lstItems;
01018 }
01019 
01020 void KonqIconViewWidget::slotDropped( QDropEvent *ev, const QValueList<QIconDragItem> & )
01021 {
01022     // Drop on background
01023     KonqOperations::doDrop( m_rootItem /* may be 0L */, url(), ev, this );
01024 }
01025 
01026 void KonqIconViewWidget::drawBackground( QPainter *p, const QRect &r )
01027 {
01028     drawBackground(p, r, r.topLeft());
01029 }
01030 
01031 void KonqIconViewWidget::drawBackground( QPainter *p, const QRect &r , const QPoint &pt)
01032 {
01033     const QPixmap *pm  = backgroundPixmap();
01034     bool hasPixmap = pm && !pm->isNull();
01035     if ( !hasPixmap ) {
01036         pm = viewport()->backgroundPixmap();
01037         hasPixmap = pm && !pm->isNull();
01038     }
01039 
01040     QRect rtgt(r);
01041     rtgt.moveTopLeft(pt);
01042     if (!hasPixmap && backgroundMode() != NoBackground) {
01043         p->fillRect(rtgt, viewport()->backgroundColor());
01044         return;
01045     }
01046 
01047     if (hasPixmap) {
01048         int ax = (r.x() + contentsX() + leftMargin()) % pm->width();
01049         int ay = (r.y() + contentsY() + topMargin()) % pm->height();
01050         p->drawTiledPixmap(rtgt, *pm, QPoint(ax, ay));
01051     }
01052 }
01053 
01054 QDragObject * KonqIconViewWidget::dragObject()
01055 {
01056     if ( !currentItem() )
01057         return 0;
01058 
01059     return konqDragObject( viewport() );
01060 }
01061 
01062 KonqIconDrag * KonqIconViewWidget::konqDragObject( QWidget * dragSource )
01063 {
01064     //kdDebug(1203) << "KonqIconViewWidget::konqDragObject" << endl;
01065 
01066     KonqIconDrag * drag = new KonqIconDrag( dragSource );
01067     QIconViewItem *primaryItem = currentItem();
01068     // Append all items to the drag object
01069     for ( QIconViewItem *it = firstItem(); it; it = it->nextItem() ) {
01070         if ( it->isSelected() ) {
01071           if (!primaryItem)
01072             primaryItem = it;
01073           QString itemURL = (static_cast<KFileIVI *>(it))->item()->url().url(0, 106); // 106 is mib enum for utf8 codec
01074           kdDebug(1203) << "itemURL=" << itemURL << endl;
01075           QIconDragItem id;
01076           id.setData( QCString(itemURL.latin1()) );
01077           drag->append( id,
01078                         QRect( it->pixmapRect( FALSE ).topLeft() - m_mousePos,
01079                                it->pixmapRect( FALSE ).size() ),
01080                         QRect( it->textRect( FALSE ).topLeft() - m_mousePos,
01081                                it->textRect( FALSE ).size() ),
01082                         itemURL );
01083         }
01084     }
01085 
01086     if (primaryItem)
01087     {
01088        // Position of the item clicked in the view
01089        QPoint itempos = primaryItem->pixmapRect( FALSE ).topLeft();
01090        // Set pixmap, with the correct offset
01091        drag->setPixmap( *primaryItem->pixmap(), m_mousePos - itempos );
01092     }
01093 
01094     return drag;
01095 }
01096 
01097 void KonqIconViewWidget::contentsDragEnterEvent( QDragEnterEvent *e )
01098 {
01099     if ( e->provides( "text/uri-list" ) )
01100     {
01101         QByteArray payload = e->encodedData( "text/uri-list" );
01102         if ( !payload.size() )
01103             kdError() << "Empty data !" << endl;
01104         // Cache the URLs, since we need them every time we move over a file
01105         // (see KFileIVI)
01106         bool ok = KURLDrag::decode( e, m_lstDragURLs );
01107         if( !ok )
01108             kdError() << "Couldn't decode urls dragged !" << endl;
01109     }
01110     KIconView::contentsDragEnterEvent( e );
01111 }
01112 
01113 void KonqIconViewWidget::setItemColor( const QColor &c )
01114 {
01115     iColor = c;
01116 }
01117 
01118 QColor KonqIconViewWidget::itemColor() const
01119 {
01120     return iColor;
01121 }
01122 
01123 void KonqIconViewWidget::disableIcons( const KURL::List & lst )
01124 {
01125   for ( QIconViewItem *kit = firstItem(); kit; kit = kit->nextItem() )
01126   {
01127       bool bFound = false;
01128       // Wow. This is ugly. Matching two lists together....
01129       // Some sorting to optimise this would be a good idea ?
01130       for (KURL::List::ConstIterator it = lst.begin(); !bFound && it != lst.end(); ++it)
01131       {
01132           if ( static_cast<KFileIVI *>( kit )->item()->url() == *it )
01133           {
01134               bFound = true;
01135               // maybe remove "it" from lst here ?
01136           }
01137       }
01138       static_cast<KFileIVI *>( kit )->setDisabled( bFound );
01139   }
01140 }
01141 
01142 void KonqIconViewWidget::slotSelectionChanged()
01143 {
01144     // This code is very related to ListViewBrowserExtension::updateActions
01145     int canCopy = 0;
01146     int canDel = 0;
01147     bool bInTrash = false;
01148     int iCount = 0;
01149 
01150     for ( QIconViewItem *it = firstItem(); it; it = it->nextItem() )
01151     {
01152         if ( it->isSelected() )
01153         {
01154             iCount++;
01155             canCopy++;
01156 
01157             KURL url = ( static_cast<KFileIVI *>( it ) )->item()->url();
01158             if ( url.directory(false) == KGlobalSettings::trashPath() )
01159                 bInTrash = true;
01160             if ( KProtocolInfo::supportsDeleting( url ) )
01161                 canDel++;
01162         }
01163     }
01164 
01165     emit enableAction( "cut", canDel > 0 );
01166     emit enableAction( "copy", canCopy > 0 );
01167     emit enableAction( "trash", canDel > 0 && !bInTrash && m_url.isLocalFile() );
01168     emit enableAction( "del", canDel > 0 );
01169     emit enableAction( "shred", canDel > 0 );
01170     emit enableAction( "properties", iCount > 0 && KPropertiesDialog::canDisplay( selectedFileItems() ) );
01171     emit enableAction( "editMimeType", ( iCount == 1 ) );
01172     emit enableAction( "rename", ( iCount == 1 ) );
01173 }
01174 
01175 void KonqIconViewWidget::renameSelectedItem()
01176 {
01177     kdDebug(1203) << " -- KonqIconViewWidget::renameSelectedItem() -- " << endl;
01178     QIconViewItem * item = 0L;
01179     QIconViewItem *it = firstItem();
01180     for (; it; it = it->nextItem() )
01181         if ( it->isSelected() && !item )
01182         {
01183             item = it;
01184             break;
01185         }
01186     if (!item)
01187     {
01188         Q_ASSERT(item);
01189         return;
01190     }
01191     item->rename();
01192 }
01193 
01194 void KonqIconViewWidget::cutSelection()
01195 {
01196     kdDebug(1203) << " -- KonqIconViewWidget::cutSelection() -- " << endl;
01197     KonqIconDrag * obj = konqDragObject( /* no parent ! */ );
01198     obj->setMoveSelection( true );
01199     QApplication::clipboard()->setData( obj );
01200 }
01201 
01202 void KonqIconViewWidget::copySelection()
01203 {
01204     kdDebug(1203) << " -- KonqIconViewWidget::copySelection() -- " << endl;
01205     KonqIconDrag * obj = konqDragObject( /* no parent ! */ );
01206     QApplication::clipboard()->setData( obj );
01207 }
01208 
01209 void KonqIconViewWidget::pasteSelection()
01210 {
01211     paste( url() );
01212 }
01213 
01214 void KonqIconViewWidget::paste( const KURL &url )
01215 {
01216     KonqOperations::doPaste( this, url );
01217 }
01218 
01219 KURL::List KonqIconViewWidget::selectedUrls()
01220 {
01221     KURL::List lstURLs;
01222 
01223     for ( QIconViewItem *it = firstItem(); it; it = it->nextItem() )
01224         if ( it->isSelected() )
01225             lstURLs.append( (static_cast<KFileIVI *>( it ))->item()->url() );
01226     return lstURLs;
01227 }
01228 
01229 QRect KonqIconViewWidget::iconArea() const
01230 {
01231     return m_IconRect;
01232 }
01233 
01234 void KonqIconViewWidget::setIconArea(const QRect &rect)
01235 {
01236     m_IconRect = rect;
01237 }
01238 
01239 int KonqIconViewWidget::lineupMode() const
01240 {
01241     return m_LineupMode;
01242 }
01243 
01244 void KonqIconViewWidget::setLineupMode(int mode)
01245 {
01246     m_LineupMode = mode;
01247 }
01248 
01249 bool KonqIconViewWidget::sortDirectoriesFirst() const
01250 {
01251   return m_bSortDirsFirst;
01252 }
01253 
01254 void KonqIconViewWidget::setSortDirectoriesFirst( bool b )
01255 {
01256   m_bSortDirsFirst = b;
01257 }
01258 
01259 void KonqIconViewWidget::contentsDropEvent( QDropEvent * ev )
01260 {
01261   QIconViewItem *i = findItem( ev->pos() );
01262   // Short-circuit QIconView if Ctrl is pressed, so that it's possible
01263   // to drop a file into its own parent widget to copy it.
01264   if ( !i && (ev->action() == QDropEvent::Copy || ev->action() == QDropEvent::Link)
01265           && ev->source() && ev->source() == viewport())
01266   {
01267     // First we need to call QIconView though, to clear the drag shape
01268     bool bMovable = itemsMovable();
01269     setItemsMovable(false); // hack ? call it what you want :-)
01270     KIconView::contentsDropEvent( ev );
01271     setItemsMovable(bMovable);
01272 
01273     QValueList<QIconDragItem> lst;
01274     slotDropped(ev, lst);
01275   }
01276   else
01277   {
01278     KIconView::contentsDropEvent( ev );
01279     emit dropped(); // What is this for ? (David)
01280   }
01281   // Don't do this here, it's too early !
01282   // slotSaveIconPositions();
01283   // If we want to save after the new file gets listed, though,
01284   // we could reimplement contentsDropEvent in KDIconView and set m_bNeedSave. Bah.
01285 }
01286 
01287 void KonqIconViewWidget::contentsMousePressEvent( QMouseEvent *e )
01288 {
01289   //kdDebug(1203) << "KonqIconViewWidget::contentsMousePressEvent" << endl;
01290   m_mousePos = e->pos();
01291   m_bMousePressed = true;
01292   if (d->pSoundPlayer)
01293     d->pSoundPlayer->stop();
01294   d->bSoundItemClicked = true;
01295   KIconView::contentsMousePressEvent( e );
01296 }
01297 
01298 void KonqIconViewWidget::contentsMouseReleaseEvent( QMouseEvent *e )
01299 {
01300   m_bMousePressed = false;
01301   KIconView::contentsMouseReleaseEvent( e );
01302 }
01303 
01304 void KonqIconViewWidget::slotSaveIconPositions()
01305 {
01306   if ( m_dotDirectoryPath.isEmpty() )
01307     return;
01308   if ( !m_bDesktop )
01309     return; // Currently not available in Konqueror
01310   kdDebug(1214) << "KonqIconViewWidget::slotSaveIconPositions" << endl;
01311   KSimpleConfig dotDirectory( m_dotDirectoryPath );
01312   QIconViewItem *it = firstItem();
01313   if ( !it )
01314     return; // No more icons. Maybe we're closing and they've been removed already
01315   while ( it )
01316   {
01317     KFileIVI *ivi = static_cast<KFileIVI *>( it );
01318     KFileItem *item = ivi->item();
01319 
01320     dotDirectory.setGroup( QString( m_iconPositionGroupPrefix ).append( item->url().fileName() ) );
01321     kdDebug(1214) << "KonqIconViewWidget::slotSaveIconPositions " << item->url().fileName() << " " << it->x() << " " << it->y() << endl;
01322     dotDirectory.writeEntry( "X", it->x() );
01323     dotDirectory.writeEntry( "Y", it->y() );
01324     dotDirectory.writeEntry( "Exists", true );
01325 
01326     it = it->nextItem();
01327   }
01328 
01329   QStringList groups = dotDirectory.groupList();
01330   QStringList::ConstIterator gIt = groups.begin();
01331   QStringList::ConstIterator gEnd = groups.end();
01332   for (; gIt != gEnd; ++gIt )
01333     if ( (*gIt).left( m_iconPositionGroupPrefix.length() ) == m_iconPositionGroupPrefix )
01334     {
01335       dotDirectory.setGroup( *gIt );
01336       if ( dotDirectory.hasKey( "Exists" ) )
01337         dotDirectory.deleteEntry( "Exists", false );
01338       else
01339       {
01340         kdDebug(1214) << "KonqIconViewWidget::slotSaveIconPositions deleting group " << *gIt << endl;
01341         dotDirectory.deleteGroup( *gIt );
01342       }
01343     }
01344 
01345   dotDirectory.sync();
01346 }
01347 
01348 // Adapted version of QIconView::insertInGrid, that works relative to
01349 // m_IconRect, instead of the entire viewport.
01350 
01351 void KonqIconViewWidget::insertInGrid(QIconViewItem *item)
01352 {
01353     if (0L == item)
01354         return;
01355 
01356     if (!m_IconRect.isValid())
01357     {
01358         QIconView::insertInGrid(item);
01359         return;
01360     }
01361 
01362     QRegion r(m_IconRect);
01363     QIconViewItem *i = firstItem();
01364     int y = -1;
01365     for (; i; i = i->nextItem() )
01366     {
01367         r = r.subtract(i->rect());
01368         y = QMAX(y, i->y() + i->height());
01369     }
01370 
01371     QMemArray<QRect> rects = r.rects();
01372     QMemArray<QRect>::Iterator it = rects.begin();
01373     bool foundPlace = FALSE;
01374     for (; it != rects.end(); ++it)
01375     {
01376         QRect rect = *it;
01377         if (rect.width() >= item->width() && rect.height() >= item->height())
01378         {
01379             int sx = 0, sy = 0;
01380             if (rect.width() >= item->width() + spacing())
01381                 sx = spacing();
01382             if (rect.height() >= item->height() + spacing())
01383                 sy = spacing();
01384             item->move(rect.x() + sx, rect.y() + sy);
01385             foundPlace = true;
01386             break;
01387         }
01388     }
01389 
01390     if (!foundPlace)
01391         item->move(m_IconRect.topLeft());
01392 
01393     //item->dirty = false;
01394     return;
01395 }
01396 
01397 
01398 /*
01399  * Utility class QIVItemBin is used by KonqIconViewWidget::lineupIcons().
01400  */
01401 
01402 class QIVItemBin
01403 {
01404 public:
01405     QIVItemBin() {}
01406     ~QIVItemBin() {}
01407 
01408     int count() { return mData.count(); }
01409     void add(QIconViewItem *item) { mData.append(item); }
01410 
01411     QIconViewItem *top();
01412     QIconViewItem *bottom();
01413     QIconViewItem *left();
01414     QIconViewItem *right();
01415 
01416 private:
01417     QPtrList<QIconViewItem> mData;
01418 };
01419 
01420 QIconViewItem *QIVItemBin::top()
01421 {
01422     if (mData.count() == 0)
01423         return 0L;
01424 
01425     QIconViewItem *it = mData.first();
01426     QIconViewItem *item = it;
01427     int y = it->y();
01428     for (it=mData.next(); it; it=mData.next())
01429     {
01430         if (it->y() < y)
01431         {
01432             y = it->y();
01433             item = it;
01434         }
01435     }
01436     mData.remove(item);
01437     return item;
01438 }
01439 
01440 QIconViewItem *QIVItemBin::bottom()
01441 {
01442     if (mData.count() == 0)
01443         return 0L;
01444 
01445     QIconViewItem *it = mData.first();
01446     QIconViewItem *item = it;
01447     int y = it->y();
01448     for (it=mData.next(); it; it=mData.next())
01449     {
01450         if (it->y() > y)
01451         {
01452             y = it->y();
01453             item = it;
01454         }
01455     }
01456     mData.remove(item);
01457     return item;
01458 }
01459 
01460 QIconViewItem *QIVItemBin::left()
01461 {
01462     if (mData.count() == 0)
01463         return 0L;
01464 
01465     QIconViewItem *it=mData.first();
01466     QIconViewItem *item = it;
01467     int x = it->x();
01468     for (it=mData.next(); it; it=mData.next())
01469     {
01470         if (it->x() < x)
01471         {
01472             x = it->x();
01473             item = it;
01474         }
01475     }
01476     mData.remove(item);
01477     return item;
01478 }
01479 
01480 QIconViewItem *QIVItemBin::right()
01481 {
01482     if (mData.count() == 0)
01483         return 0L;
01484 
01485     QIconViewItem *it=mData.first();
01486     QIconViewItem *item = it;
01487     int x = it->x();
01488     for (it=mData.next(); it; it=mData.next())
01489     {
01490         if (it->x() > x)
01491         {
01492             x = it->x();
01493             item = it;
01494         }
01495     }
01496     mData.remove(item);
01497     return item;
01498 }
01499 
01500 
01501 /*
01502  * The algorithm used for lineing up the icons could be called
01503  * "beating flat the icon field". Imagine the icon field to be some height
01504  * field on a regular grid, with the height being the number of icons in
01505  * each grid element. Now imagine slamming on the field with a shovel or
01506  * some other flat surface. The high peaks will be flattened and spread out
01507  * over their adjacent areas. This is basically what the algorithm tries to
01508  * simulate.
01509  *
01510  * First, the icons are binned to a grid of the desired size. If all bins
01511  * are containing at most one icon, we're done, of course. We just have to
01512  * move all icons to the center of each grid element.
01513  * For each bin which has more than one icon in it, we calculate 4
01514  * "friction coefficients", one for each cardinal direction. The friction
01515  * coefficient of a direction is the number of icons adjacent in that
01516  * direction. The idea is that this number is somewhat a measure in which
01517  * direction the icons should flow: icons flow in the direction of lowest
01518  * friction coefficient. We move a maximum of one icon per bin and loop over
01519  * all bins. This procedure is repeated some maximum number of times or until
01520  * no icons are moved anymore.
01521  *
01522  * I don't know if this algorithm is good or bad, I don't even know if it will
01523  * work all the time. It seems a correct thing to do, however, and it seems to
01524  * work particularly well. In any case, the number of runs is limited so there
01525  * can be no races.
01526  */
01527 
01528 //#define MIN3(a,b,c) ((a) < QMIN((b),(c)) ? (a) : ((b) < QMIN((a),(c)) ? (b) : (c)))
01529 #define MIN3(a,b,c) (QMIN((a),(QMIN((b),(c)))))
01530 
01531 void KonqIconViewWidget::lineupIcons()
01532 {
01533     if ( !firstItem() )
01534     {
01535         kdDebug(1203) << "No icons at all ?\n";
01536         return;
01537     }
01538 
01539     // Make a list of items, and look at the highest one
01540     QValueList<QIconViewItem*> items;
01541     int dy = 0;
01542 
01543     // Put each ivi in its corresponding bin.
01544     QIconViewItem *item;
01545     for (item=firstItem(); item; item=item->nextItem())
01546     {
01547         items.append(item);
01548         dy = QMAX( dy, item->height() );
01549     }
01550 
01551     // For dx, use what used to be the gridX
01552     int dx = gridXValue();
01553 
01554     dx += spacing();
01555     dy += spacing();
01556 
01557     kdDebug(1203) << "dx = " << dx << ", dy = " << dy << "\n";
01558 
01559     if ((dx < 15) || (dy < 15))
01560     {
01561         kdWarning(1203) << "Do you really have that fine a grid?\n";
01562         return;
01563     }
01564 
01565     int x1, x2, y1, y2;
01566     if (m_IconRect.isValid())
01567     {
01568         x1 = m_IconRect.left(); x2 = m_IconRect.right();
01569         y1 = m_IconRect.top(); y2 = m_IconRect.bottom();
01570     } else
01571     {
01572         x1 = 0; x2 = viewport()->width();
01573         y1 = 0; y2 = viewport()->height();
01574     }
01575 
01576     int nx = (x2 - x1) / dx;
01577     int ny = (y2 - y1) / dy;
01578 
01579     kdDebug(1203) << "nx = " << nx << " ny = " << ny << "\n";
01580     if ((nx > 150) || (ny > 100))
01581     {
01582         kdDebug(1203) << "Do you really have that fine a grid?\n";
01583         return;
01584     }
01585     if ((nx <= 1) || (ny <= 1))
01586     {
01587         kdDebug(1203) << "Iconview is too small, not doing anything.\n";
01588         return;
01589     }
01590 
01591     // Create a grid of (ny x nx) bins.
01592     typedef QIVItemBin *QIVItemPtr;
01593     QIVItemPtr **bins = new QIVItemPtr*[ny];
01594 
01595     int i, j;
01596     for (j=0; j<ny; j++)
01597     {
01598         bins[j] = new QIVItemPtr[nx];
01599         for (i=0; i<nx; i++)
01600             bins[j][i] = 0;
01601     }
01602 
01603     int left = x1;
01604     int right = x1 + dx;
01605     i = 0;
01606 
01607     while (items.count())
01608     {
01609         int max_icon_x = dx;
01610         right = left + dx;
01611 
01612         for (QValueList<QIconViewItem*>::Iterator it = items.begin(); it != items.end(); ++it)
01613         {
01614             item = *it;
01615             if (item->x() < right && max_icon_x < item->width() )
01616                 max_icon_x = item->width();
01617         }
01618 
01619         right = left + max_icon_x;
01620 
01621         for (QValueList<QIconViewItem*>::Iterator it = items.begin(); it != items.end();)
01622         {
01623             item = *it;
01624             int mid = item->x() + item->width()/2 - x1;
01625             kdDebug(1203) << "matching " << mid << " left " << left << " right " << right << endl;
01626             if (mid < left || (mid >= left && mid < right)) {
01627                 it = items.remove(it);
01628                 j = (item->y() + item->height()/2 - y1) / dy;
01629                 if (j < 0) j = 0;
01630                 else if (j >= ny) j = ny - 1;
01631 
01632                 kdDebug(1203) << "putting " << item->text() << " " << i << " " << j << endl;
01633                 if (bins[j][i] == 0L)
01634                     bins[j][i] = new QIVItemBin;
01635                 bins[j][i]->add(item);
01636             } else
01637                 ++it;
01638         }
01639         kdDebug(1203) << "next round " << items.count() << endl;
01640         i = QMIN(i+1, nx - 1);
01641         left += max_icon_x + spacing();
01642     }
01643 
01644     // The shuffle code
01645     int n, k;
01646     int infinity = 100000, nmoves = 1;
01647     for (n=0; (n < 10) && (nmoves != 0); n++)
01648     {
01649         nmoves = 0;
01650         for (j=0; j<ny; j++)
01651         {
01652             for (i=0; i<nx; i++)
01653             {
01654                 if (!bins[j][i] || (bins[j][i]->count() < 2))
01655                     continue;
01656 
01657                 kdDebug(1203) << "calc for " << i << " " << j << endl;
01658                 // Calculate the 4 "friction coefficients".
01659                 int tf = 0;
01660                 for (k=j-1; (k >= 0) && bins[k][i] && bins[k][i]->count(); k--)
01661                     tf += bins[k][i]->count();
01662                 if (k == -1)
01663                     tf += infinity;
01664 
01665                 int bf = 0;
01666                 for (k=j+1; (k < ny) && bins[k][i] && bins[k][i]->count(); k++)
01667                     bf += bins[k][i]->count();
01668                 if (k == ny)
01669                     bf += infinity;
01670 
01671                 int lf = 0;
01672                 for (k=i-1; (k >= 0) && bins[j][k] && bins[j][k]->count(); k--)
01673                     lf += bins[j][k]->count();
01674                 if (k == -1)
01675                     lf += infinity;
01676 
01677                 int rf = 0;
01678                 for (k=i+1; (k < nx) && bins[j][k] && bins[j][k]->count(); k++)
01679                     rf += bins[j][k]->count();
01680                 if (k == nx)
01681                     rf += infinity;
01682 
01683                 // If we are stuck between walls, continue
01684                 if ( (tf >= infinity) && (bf >= infinity) &&
01685                      (lf >= infinity) && (rf >= infinity)
01686                    )
01687                     continue;
01688 
01689                 // Is there a preferred lineup direction?
01690                 if (m_LineupMode == LineupHorizontal)
01691                 {
01692                     tf += infinity;
01693                     bf += infinity;
01694                 } else if (m_LineupMode == LineupVertical)
01695                 {
01696                     lf += infinity;
01697                     rf += infinity;
01698                 }
01699 
01700                 // Move one item in the direction of the least friction.
01701                 if (tf <= MIN3(bf,lf,rf))
01702                 {
01703                     if (!bins[j-1][i])
01704                         bins[j-1][i] = new QIVItemBin;
01705                     bins[j-1][i]->add(bins[j][i]->top());
01706                 } else if (bf <= MIN3(tf,lf,rf))
01707                 {
01708                     if (!bins[j+1][i])
01709                         bins[j+1][i] = new QIVItemBin;
01710                     bins[j+1][i]->add(bins[j][i]->bottom());
01711                 } else if (lf <= MIN3(tf,bf,rf))
01712                 {
01713                     if (!bins[j][i-1])
01714                         bins[j][i-1] = new QIVItemBin;
01715                     bins[j][i-1]->add(bins[j][i]->left());
01716                 } else
01717                 {
01718                     if (!bins[j][i+1])
01719                         bins[j][i+1] = new QIVItemBin;
01720                     bins[j][i+1]->add(bins[j][i]->right());
01721                 }
01722 
01723                 nmoves++;
01724             }
01725         }
01726         kdDebug(1203) << "nmoves = " << nmoves << "\n";
01727     }
01728 
01729     // Perform the actual moving
01730     n = 0;
01731     QIconViewItem **its = new QIconViewItem*[ny];
01732     for (i=0; i<nx; i++)
01733     {
01734         int max_icon_x = dx;
01735         for (j=0; j<ny; j++)
01736         {
01737             its[j] = 0;
01738             if (!bins[j][i] || !bins[j][i]->count())
01739                 continue;
01740 
01741             item = its[j] = bins[j][i]->top();
01742             if ( max_icon_x < item->width() )
01743                 max_icon_x = item->width();
01744         }
01745 
01746         for (j=0; j<ny; j++)
01747         {
01748             if ( its[j] == 0 )
01749                 continue;
01750 
01751             item = its[j];
01752             int x = x1 + spacing() + ( max_icon_x - item->width() )/2;
01753             int y = y1 + j * dy;
01754             if (item->pos() != QPoint(x, y))
01755             {
01756                 kdDebug(1203) << "moving " << item->text() << " " << x << " " << y << endl;
01757                 item->move(x, y);
01758             }
01759             if (bins[j][i]->count())
01760             {
01761                 kdDebug(1203) << "Lineup incomplete..\n";
01762                 item = bins[j][i]->top();
01763                 for (k=1; item; k++)
01764                 {
01765                     x = x1 + i*dx + spacing() + 10*k; y = y1 + j*dy + spacing() + 5*k;
01766                     if (item->pos() != QPoint(x, y))
01767                     {
01768                         item->move(x, y);
01769                     }
01770                     item = bins[j][i]->top();
01771                 }
01772             }
01773             delete bins[j][i];
01774             bins[j][i] = 0;
01775             n++;
01776         }
01777         x1 += max_icon_x  + spacing();
01778     }
01779     delete[] its;
01780 
01781     updateContents();
01782     for (int j=0; j<ny; j++)
01783         delete [] bins[j];
01784     delete[] bins;
01785     kdDebug(1203) << n << " icons successfully moved.\n";
01786     return;
01787 }
01788 
01789 void KonqIconViewWidget::visualActivate(QIconViewItem * item)
01790 {
01791     // Rect of the QIconViewItem.
01792     QRect irect = item->rect();
01793 
01794     // Rect of the QIconViewItem's pixmap area.
01795     QRect rect = item->pixmapRect();
01796 
01797     // Adjust to correct position. If this isn't done, the fact that the
01798     // text may be wider than the pixmap puts us off-centre.
01799     rect.moveBy(irect.x(), irect.y());
01800 
01801     // Adjust for scrolling (David)
01802     rect.moveBy( -contentsX(), -contentsY() );
01803 
01804     KIconEffect::visualActivate(viewport(), rect);
01805 }
01806 
01807 void KonqIconViewWidget::backgroundPixmapChange( const QPixmap & )
01808 {
01809     viewport()->update();
01810 }
01811 
01812 void KonqIconViewWidget::setPreviewSettings( const QStringList& settings )
01813 {
01814     d->previewSettings = settings;
01815 }
01816 
01817 const QStringList& KonqIconViewWidget::previewSettings()
01818 {
01819     return d->previewSettings;
01820 }
01821 
01822 void KonqIconViewWidget::setNewURL( const QString& url )
01823 {
01824     KURL u;
01825     if ( url.startsWith( "/" ) )
01826         u.setPath( url );
01827     else
01828         u = url;
01829     setURL( u );
01830 }
01831 
01832 #include "konq_iconviewwidget.moc"
01833 
01834 /* vim: set et sw=4 ts=8 softtabstop=4: */
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Thu Jan 29 23:03:28 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001