kdeui Library API Documentation

klistview.cpp

00001 /* This file is part of the KDE libraries
00002    Copyright (C) 2000 Reginald Stadlbauer <reggie@kde.org>
00003    Copyright (C) 2000,2003 Charles Samuels <charles@kde.org>
00004    Copyright (C) 2000 Peter Putzer
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License version 2 as published by the Free Software Foundation.
00009 
00010    This library 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    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00018    Boston, MA 02111-1307, USA.
00019 */
00020 #include "config.h"
00021 
00022 #include <qdragobject.h>
00023 #include <qtimer.h>
00024 #include <qheader.h>
00025 #include <qcursor.h>
00026 #include <qtooltip.h>
00027 #include <qstyle.h>
00028 #include <qpainter.h>
00029 
00030 #include <kglobalsettings.h>
00031 #include <kconfig.h>
00032 #include <kcursor.h>
00033 #include <kapplication.h>
00034 
00035 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00036 #include <kipc.h> // schroder
00037 #endif
00038 
00039 #include <kdebug.h>
00040 
00041 #include "klistview.h"
00042 #include "klistviewlineedit.h"
00043 
00044 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00045 #include <X11/Xlib.h> // schroder
00046 #endif
00047 
00048 class KListView::Tooltip : public QToolTip
00049 {
00050 public:
00051   Tooltip (KListView* parent, QToolTipGroup* group = 0L);
00052   virtual ~Tooltip () {}
00053 
00054 protected:
00058   virtual void maybeTip (const QPoint&);
00059 
00060 private:
00061   KListView* mParent;
00062 };
00063 
00064 KListView::Tooltip::Tooltip (KListView* parent, QToolTipGroup* group)
00065   : QToolTip (parent, group),
00066         mParent (parent)
00067 {
00068 }
00069 
00070 void KListView::Tooltip::maybeTip (const QPoint&)
00071 {
00072   // FIXME
00073 }
00074 
00075 class KListView::KListViewPrivate
00076 {
00077 public:
00078   KListViewPrivate (KListView* listview)
00079     : pCurrentItem (0L),
00080       dragDelay (KGlobalSettings::dndEventDelay()),
00081       editor (new KListViewLineEdit (listview)),
00082       cursorInExecuteArea(false),
00083       itemsMovable (true),
00084       selectedBySimpleMove(false),
00085       selectedUsingMouse(false),
00086       itemsRenameable (false),
00087       validDrag (false),
00088       dragEnabled (false),
00089       autoOpen (true),
00090       disableAutoSelection (false),
00091       dropVisualizer (true),
00092       dropHighlighter (false),
00093       createChildren (true),
00094       pressedOnSelected (false),
00095       wasShiftEvent (false),
00096       fullWidth (false),
00097       sortAscending(true),
00098         tabRename(true),
00099       sortColumn(0),
00100       selectionDirection(0),
00101       tooltipColumn (0),
00102       selectionMode (Single),
00103       contextMenuKey (KGlobalSettings::contextMenuKey()),
00104       showContextMenusOnPress (KGlobalSettings::showContextMenusOnPress()),
00105       mDropVisualizerWidth (4),
00106       paintAbove (0),
00107       paintCurrent (0),
00108       paintBelow (0),
00109       painting (false)
00110   {
00111       renameable.append(0);
00112       connect(editor, SIGNAL(done(QListViewItem*,int)), listview, SLOT(doneEditing(QListViewItem*,int)));
00113   }
00114 
00115   ~KListViewPrivate ()
00116   {
00117     delete editor;
00118   }
00119 
00120   QListViewItem* pCurrentItem;
00121 
00122   QTimer autoSelect;
00123   int autoSelectDelay;
00124 
00125   QTimer dragExpand;
00126   QListViewItem* dragOverItem;
00127   QPoint dragOverPoint;
00128 
00129   QPoint startDragPos;
00130   int dragDelay;
00131 
00132   KListViewLineEdit *editor;
00133   QValueList<int> renameable;
00134 
00135   bool cursorInExecuteArea:1;
00136   bool bUseSingle:1;
00137   bool bChangeCursorOverItem:1;
00138   bool itemsMovable:1;
00139   bool selectedBySimpleMove : 1;
00140   bool selectedUsingMouse:1;
00141   bool itemsRenameable:1;
00142   bool validDrag:1;
00143   bool dragEnabled:1;
00144   bool autoOpen:1;
00145   bool disableAutoSelection:1;
00146   bool dropVisualizer:1;
00147   bool dropHighlighter:1;
00148   bool createChildren:1;
00149   bool pressedOnSelected:1;
00150   bool wasShiftEvent:1;
00151   bool fullWidth:1;
00152   bool sortAscending:1;
00153   bool tabRename:1;
00154 
00155   int sortColumn;
00156 
00157   //+1 means downwards (y increases, -1 means upwards, 0 means not selected), aleXXX
00158   int selectionDirection;
00159   int tooltipColumn;
00160 
00161   SelectionModeExt selectionMode;
00162   int contextMenuKey;
00163   bool showContextMenusOnPress;
00164 
00165   QRect mOldDropVisualizer;
00166   int mDropVisualizerWidth;
00167   QRect mOldDropHighlighter;
00168   QListViewItem *afterItemDrop;
00169   QListViewItem *parentItemDrop;
00170 
00171   QListViewItem *paintAbove;
00172   QListViewItem *paintCurrent;
00173   QListViewItem *paintBelow;
00174   bool painting;
00175 
00176   QColor alternateBackground;
00177 };
00178 
00179 
00180 KListViewLineEdit::KListViewLineEdit(KListView *parent)
00181         : KLineEdit(parent->viewport()), item(0), col(0), p(parent)
00182 {
00183         setFrame( false );
00184         hide();
00185         connect( parent, SIGNAL( selectionChanged() ), SLOT( slotSelectionChanged() ));
00186 }
00187 
00188 KListViewLineEdit::~KListViewLineEdit()
00189 {
00190 }
00191 
00192 QListViewItem *KListViewLineEdit::currentItem() const
00193 {
00194     return item;
00195 }
00196 
00197 void KListViewLineEdit::load(QListViewItem *i, int c)
00198 {
00199         item=i;
00200         col=c;
00201 
00202         QRect rect(p->itemRect(i));
00203         setText(item->text(c));
00204         home( true );
00205         
00206         int fieldX = rect.x() - 1;
00207         int fieldW = p->columnWidth(col) + 2;
00208 
00209         int pos = p->header()->mapToIndex(col);
00210         for ( int index = 0; index < pos; index++ )
00211             fieldX += p->columnWidth( p->header()->mapToSection( index ));
00212 
00213         if ( col == 0 ) {
00214             int d = i->depth() + (p->rootIsDecorated() ? 1 : 0);
00215             d *= p->treeStepSize();
00216             fieldX += d;
00217             fieldW -= d;
00218         }
00219 
00220         if ( i->pixmap( col ) ) {// add width of pixmap
00221             int d = i->pixmap( col )->width();
00222             fieldX += d;
00223             fieldW -= d;
00224         }
00225 
00226         setGeometry(fieldX, rect.y() - 1, fieldW, rect.height() + 2);
00227         show();
00228         setFocus();
00229 }
00230 
00231 /*  Helper functions to for
00232  *  tabOrderedRename functionality.
00233  */
00234 
00235 static int nextCol (KListView *pl, QListViewItem *pi, int start, int dir)
00236 {
00237     if (pi)
00238     {
00239         //  Find the next renameable column in the current row
00240         for (; ((dir == +1) ? (start < pl->columns()) : (start >= 0)); start += dir)
00241             if (pl->isRenameable(start))
00242                 return start;
00243     }
00244 
00245     return -1;
00246 }
00247 
00248 static QListViewItem *prevItem (QListViewItem *pi)
00249 {
00250     QListViewItem *pa = pi->itemAbove();
00251 
00252     /*  Does what the QListViewItem::previousSibling()
00253      *  of my dreams would do.
00254      */
00255     if (pa && pa->parent() == pi->parent())
00256         return pa;
00257 
00258     return 0;
00259 }
00260 
00261 static QListViewItem *lastQChild (QListViewItem *pi)
00262 {
00263     if (pi)
00264     {
00265         /*  Since there's no QListViewItem::lastChild().
00266          *  This finds the last sibling for the given
00267          *  item.
00268          */
00269         for (QListViewItem *pt = pi->nextSibling(); pt; pt = pt->nextSibling())
00270             pi = pt;
00271     }
00272 
00273     return pi;
00274 }
00275 
00276 void KListViewLineEdit::selectNextCell (QListViewItem *pitem, int column, bool forward)
00277 {
00278     const int ncols = p->columns();
00279     const int dir = forward ? +1 : -1;
00280     const int restart = forward ? 0 : (ncols - 1);
00281     QListViewItem *top = (pitem && pitem->parent())
00282         ? pitem->parent()->firstChild()
00283         : p->firstChild();
00284     QListViewItem *pi = pitem;
00285 
00286     terminate();        //  Save current changes
00287 
00288     do
00289     {
00290         /*  Check the rest of the current row for an editable column,
00291          *  if that fails, check the entire next/previous row. The
00292          *  last case goes back to the first item in the current branch
00293          *  or the last item in the current branch depending on the
00294          *  direction.
00295          */
00296         if ((column = nextCol(p, pi, column + dir, dir)) != -1 ||
00297             (column = nextCol(p, (pi = (forward ? pi->nextSibling() : prevItem(pi))), restart, dir)) != -1 ||
00298             (column = nextCol(p, (pi = (forward ? top : lastQChild(pitem))), restart, dir)) != -1)
00299         {
00300             if (pi)
00301             {
00302                 p->setCurrentItem(pi);      //  Calls terminate
00303                 p->rename(pi, column);
00304 
00305                 /*  Some listviews may override rename() to
00306                  *  prevent certain items from being renamed,
00307                  *  if this is done, [m_]item will be NULL
00308                  *  after the rename() call... try again.
00309                  */
00310                 if (!item)
00311                     continue;
00312 
00313                 break;
00314             }
00315         }
00316     }
00317     while (pi && !item);
00318 }
00319 
00320 #ifdef KeyPress
00321 #undef KeyPress
00322 #endif
00323 
00324 bool KListViewLineEdit::event (QEvent *pe)
00325 {
00326     if (pe->type() == QEvent::KeyPress)
00327     {
00328         QKeyEvent *k = (QKeyEvent *) pe;
00329 
00330         if ((k->key() == Qt::Key_Backtab || k->key() == Qt::Key_Tab) &&
00331             p->tabOrderedRenaming() && p->itemsRenameable() &&
00332             !(k->state() & ControlButton || k->state() & AltButton))
00333         {
00334             selectNextCell(item, col,
00335                 (k->key() == Key_Tab && !(k->state() & ShiftButton)));
00336             return true;
00337         }
00338     }
00339 
00340     return KLineEdit::event(pe);
00341 }
00342 
00343 void KListViewLineEdit::keyPressEvent(QKeyEvent *e)
00344 {
00345     if(e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter )
00346         terminate(true);
00347     else if(e->key() == Qt::Key_Escape)
00348         terminate(false);
00349         else if (e->key() == Qt::Key_Down || e->key() == Qt::Key_Up)
00350         {
00351         terminate(true);
00352                 KLineEdit::keyPressEvent(e);
00353         }
00354     else
00355         KLineEdit::keyPressEvent(e);
00356 }
00357 
00358 void KListViewLineEdit::terminate()
00359 {
00360     terminate(true);
00361 }
00362 
00363 void KListViewLineEdit::terminate(bool commit)
00364 {
00365     if ( item )
00366     {
00367         //kdDebug() << "KListViewLineEdit::terminate " << commit << endl;
00368         if (commit)
00369             item->setText(col, text());
00370         int c=col;
00371         QListViewItem *i=item;
00372         col=0;
00373         item=0;
00374         hide(); // will call focusOutEvent, that's why we set item=0 before
00375         if (commit)
00376             emit done(i,c);
00377     }
00378 }
00379 
00380 void KListViewLineEdit::focusOutEvent(QFocusEvent *ev)
00381 {
00382     QFocusEvent * focusEv = static_cast<QFocusEvent*>(ev);
00383     // Don't let a RMB close the editor
00384     if (focusEv->reason() != QFocusEvent::Popup && focusEv->reason() != QFocusEvent::ActiveWindow)
00385         terminate(true);
00386     else
00387         KLineEdit::focusOutEvent(ev);
00388 }
00389 
00390 void KListViewLineEdit::paintEvent( QPaintEvent *e )
00391 {
00392     KLineEdit::paintEvent( e );
00393 
00394     if ( !frame() ) {
00395         QPainter p( this );
00396         p.setClipRegion( e->region() );
00397         p.drawRect( rect() );
00398     }
00399 }
00400 
00401 // selection changed -> terminate. As our "item" can be already deleted,
00402 // we can't call terminate(false), because that would emit done() with
00403 // a dangling pointer to "item".
00404 void KListViewLineEdit::slotSelectionChanged()
00405 {
00406     item = 0;
00407     col = 0;
00408     hide();
00409 }
00410 
00411 
00412 KListView::KListView( QWidget *parent, const char *name )
00413   : QListView( parent, name ),
00414         d (new KListViewPrivate (this))
00415 {
00416   setDragAutoScroll(true);
00417 
00418   connect( this, SIGNAL( onViewport() ),
00419                    this, SLOT( slotOnViewport() ) );
00420   connect( this, SIGNAL( onItem( QListViewItem * ) ),
00421                    this, SLOT( slotOnItem( QListViewItem * ) ) );
00422 
00423   connect (this, SIGNAL(contentsMoving(int,int)),
00424                    this, SLOT(cleanDropVisualizer()));
00425   connect (this, SIGNAL(contentsMoving(int,int)),
00426                    this, SLOT(cleanItemHighlighter()));
00427 
00428   slotSettingsChanged(KApplication::SETTINGS_MOUSE);
00429   if (kapp)
00430   {
00431     connect( kapp, SIGNAL( settingsChanged(int) ), SLOT( slotSettingsChanged(int) ) );
00432 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00433     kapp->addKipcEventMask( KIPC::SettingsChanged );
00434 #endif
00435   }
00436 
00437   connect(&d->autoSelect, SIGNAL( timeout() ),
00438                   this, SLOT( slotAutoSelect() ) );
00439   connect(&d->dragExpand, SIGNAL( timeout() ),
00440                   this, SLOT( slotDragExpand() ) );
00441 
00442   // context menu handling
00443   if (d->showContextMenusOnPress)
00444         {
00445           connect (this, SIGNAL (rightButtonPressed (QListViewItem*, const QPoint&, int)),
00446                            this, SLOT (emitContextMenu (QListViewItem*, const QPoint&, int)));
00447         }
00448   else
00449         {
00450           connect (this, SIGNAL (rightButtonClicked (QListViewItem*, const QPoint&, int)),
00451                            this, SLOT (emitContextMenu (QListViewItem*, const QPoint&, int)));
00452         }
00453 
00454   connect (this, SIGNAL (menuShortCutPressed (KListView*, QListViewItem*)),
00455                    this, SLOT (emitContextMenu (KListView*, QListViewItem*)));
00456   d->alternateBackground = KGlobalSettings::alternateBackgroundColor();
00457 }
00458 
00459 KListView::~KListView()
00460 {
00461   delete d;
00462 }
00463 
00464 bool KListView::isExecuteArea( const QPoint& point )
00465 {
00466   if ( itemAt( point ) )
00467     return isExecuteArea( point.x() );
00468 
00469   return false;
00470 }
00471 
00472 bool KListView::isExecuteArea( int x )
00473 {
00474   if( allColumnsShowFocus() )
00475     return true;
00476   else {
00477     int offset = 0;
00478     int width = columnWidth( 0 );
00479     int pos = header()->mapToIndex( 0 );
00480 
00481     for ( int index = 0; index < pos; index++ )
00482       offset += columnWidth( header()->mapToSection( index ) );
00483 
00484     x += contentsX(); // in case of a horizontal scrollbar
00485     return ( x > offset && x < ( offset + width ) );
00486   }
00487 }
00488 
00489 void KListView::slotOnItem( QListViewItem *item )
00490 {
00491   QPoint vp = viewport()->mapFromGlobal( QCursor::pos() );
00492   if ( item && isExecuteArea( vp.x() ) && (d->autoSelectDelay > -1) && d->bUseSingle ) {
00493     d->autoSelect.start( d->autoSelectDelay, true );
00494     d->pCurrentItem = item;
00495   }
00496 }
00497 
00498 void KListView::slotOnViewport()
00499 {
00500   if ( d->bChangeCursorOverItem )
00501     viewport()->unsetCursor();
00502 
00503   d->autoSelect.stop();
00504   d->pCurrentItem = 0L;
00505 }
00506 
00507 void KListView::slotSettingsChanged(int category)
00508 {
00509   switch (category)
00510   {
00511   case KApplication::SETTINGS_MOUSE:
00512     d->dragDelay =  KGlobalSettings::dndEventDelay();
00513     d->bUseSingle = KGlobalSettings::singleClick();
00514 
00515     disconnect(this, SIGNAL (mouseButtonClicked (int, QListViewItem*, const QPoint &, int)),
00516                this, SLOT (slotMouseButtonClicked (int, QListViewItem*, const QPoint &, int)));
00517 
00518     if( d->bUseSingle )
00519       connect (this, SIGNAL (mouseButtonClicked (int, QListViewItem*, const QPoint &, int)),
00520                this, SLOT (slotMouseButtonClicked( int, QListViewItem*, const QPoint &, int)));
00521 
00522     d->bChangeCursorOverItem = KGlobalSettings::changeCursorOverIcon();
00523     if ( !d->disableAutoSelection )
00524       d->autoSelectDelay = KGlobalSettings::autoSelectDelay();
00525 
00526     if( !d->bUseSingle || !d->bChangeCursorOverItem )
00527        viewport()->unsetCursor();
00528 
00529     break;
00530 
00531   case KApplication::SETTINGS_POPUPMENU:
00532     d->contextMenuKey = KGlobalSettings::contextMenuKey ();
00533     d->showContextMenusOnPress = KGlobalSettings::showContextMenusOnPress ();
00534 
00535     if (d->showContextMenusOnPress)
00536     {
00537       disconnect (0L, 0L, this, SLOT (emitContextMenu (QListViewItem*, const QPoint&, int)));
00538 
00539       connect(this, SIGNAL (rightButtonPressed (QListViewItem*, const QPoint&, int)),
00540               this, SLOT (emitContextMenu (QListViewItem*, const QPoint&, int)));
00541     }
00542     else
00543     {
00544       disconnect (0L, 0L, this, SLOT (emitContextMenu (QListViewItem*, const QPoint&, int)));
00545 
00546       connect(this, SIGNAL (rightButtonClicked (QListViewItem*, const QPoint&, int)),
00547               this, SLOT (emitContextMenu (QListViewItem*, const QPoint&, int)));
00548     }
00549     break;
00550 
00551   default:
00552     break;
00553   }
00554 }
00555 
00556 void KListView::slotAutoSelect()
00557 {
00558   // check that the item still exists
00559   if( itemIndex( d->pCurrentItem ) == -1 )
00560     return;
00561 
00562   if (!isActiveWindow())
00563         {
00564           d->autoSelect.stop();
00565           return;
00566         }
00567 
00568   //Give this widget the keyboard focus.
00569   if( !hasFocus() )
00570     setFocus();
00571 
00572 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00573   // FIXME(E): Implement for Qt Embedded
00574   Window root;
00575   Window child;
00576   int root_x, root_y, win_x, win_y;
00577   uint keybstate;
00578   XQueryPointer( qt_xdisplay(), qt_xrootwin(), &root, &child,
00579                                  &root_x, &root_y, &win_x, &win_y, &keybstate );
00580 #endif
00581 
00582   QListViewItem* previousItem = currentItem();
00583   setCurrentItem( d->pCurrentItem );
00584 
00585 //#ifndef Q_WS_QWS
00586 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00587   // FIXME(E): Implement for Qt Embedded
00588   if( d->pCurrentItem ) {
00589     //Shift pressed?
00590     if( (keybstate & ShiftMask) ) {
00591       bool block = signalsBlocked();
00592       blockSignals( true );
00593 
00594       //No Ctrl? Then clear before!
00595       if( !(keybstate & ControlMask) )
00596                 clearSelection();
00597 
00598       bool select = !d->pCurrentItem->isSelected();
00599       bool update = viewport()->isUpdatesEnabled();
00600       viewport()->setUpdatesEnabled( false );
00601 
00602       bool down = previousItem->itemPos() < d->pCurrentItem->itemPos();
00603       QListViewItemIterator lit( down ? previousItem : d->pCurrentItem );
00604       for ( ; lit.current(); ++lit ) {
00605                 if ( down && lit.current() == d->pCurrentItem ) {
00606                   d->pCurrentItem->setSelected( select );
00607                   break;
00608                 }
00609                 if ( !down && lit.current() == previousItem ) {
00610                   previousItem->setSelected( select );
00611                   break;
00612                 }
00613                 lit.current()->setSelected( select );
00614       }
00615 
00616       blockSignals( block );
00617       viewport()->setUpdatesEnabled( update );
00618       triggerUpdate();
00619 
00620       emit selectionChanged();
00621 
00622       if( selectionMode() == QListView::Single )
00623                 emit selectionChanged( d->pCurrentItem );
00624     }
00625     else if( (keybstate & ControlMask) )
00626       setSelected( d->pCurrentItem, !d->pCurrentItem->isSelected() );
00627     else {
00628       bool block = signalsBlocked();
00629       blockSignals( true );
00630 
00631       if( !d->pCurrentItem->isSelected() )
00632                 clearSelection();
00633 
00634       blockSignals( block );
00635 
00636       setSelected( d->pCurrentItem, true );
00637     }
00638   }
00639   else
00640     kdDebug() << "KListView::slotAutoSelect: Thatīs not supposed to happen!!!!" << endl;
00641 #endif
00642 }
00643 
00644 void KListView::slotHeaderChanged()
00645 {
00646   if (d->fullWidth && columns())
00647   {
00648     int w = 0;
00649     for (int i = 0; i < columns() - 1; ++i) w += columnWidth(i);
00650     setColumnWidth( columns() - 1, viewport()->width() - w - 1 );
00651   }
00652 }
00653 
00654 void KListView::emitExecute( QListViewItem *item, const QPoint &pos, int c )
00655 {
00656     if( isExecuteArea( viewport()->mapFromGlobal(pos) ) ) {
00657 
00658         // Double click mode ?
00659         if ( !d->bUseSingle )
00660         {
00661             emit executed( item );
00662             emit executed( item, pos, c );
00663         }
00664         else
00665         {
00666 //#ifndef Q_WS_QWS
00667 #if defined Q_WS_X11 && ! defined K_WS_QTONLY
00668         // FIXME(E): Implement for Qt Embedded
00669             Window root;
00670             Window child;
00671             int root_x, root_y, win_x, win_y;
00672             uint keybstate;
00673             XQueryPointer( qt_xdisplay(), qt_xrootwin(), &root, &child,
00674                            &root_x, &root_y, &win_x, &win_y, &keybstate );
00675 
00676             d->autoSelect.stop();
00677 
00678             //Donīt emit executed if in SC mode and Shift or Ctrl are pressed
00679             if( !( ((keybstate & ShiftMask) || (keybstate & ControlMask)) ) ) {
00680                 emit executed( item );
00681                 emit executed( item, pos, c );
00682             }
00683 #endif
00684         }
00685     }
00686 }
00687 
00688 void KListView::focusInEvent( QFocusEvent *fe )
00689 {
00690  //   kdDebug()<<"KListView::focusInEvent()"<<endl;
00691   QListView::focusInEvent( fe );
00692   if ((d->selectedBySimpleMove)
00693       && (d->selectionMode == FileManager)
00694       && (fe->reason()!=QFocusEvent::Popup)
00695       && (fe->reason()!=QFocusEvent::ActiveWindow)
00696       && (currentItem()!=0))
00697   {
00698       currentItem()->setSelected(true);
00699       currentItem()->repaint();
00700       emit selectionChanged();
00701   };
00702 }
00703 
00704 void KListView::focusOutEvent( QFocusEvent *fe )
00705 {
00706   cleanDropVisualizer();
00707   cleanItemHighlighter();
00708 
00709   d->autoSelect.stop();
00710 
00711   if ((d->selectedBySimpleMove)
00712       && (d->selectionMode == FileManager)
00713       && (fe->reason()!=QFocusEvent::Popup)
00714       && (fe->reason()!=QFocusEvent::ActiveWindow)
00715       && (currentItem()!=0)
00716       && (!d->editor->isVisible()))
00717   {
00718       currentItem()->setSelected(false);
00719       currentItem()->repaint();
00720       emit selectionChanged();
00721   };
00722 
00723   QListView::focusOutEvent( fe );
00724 }
00725 
00726 void KListView::leaveEvent( QEvent *e )
00727 {
00728   d->autoSelect.stop();
00729 
00730   QListView::leaveEvent( e );
00731 }
00732 
00733 bool KListView::event( QEvent *e )
00734 {
00735   if (e->type() == QEvent::ApplicationPaletteChange)
00736     d->alternateBackground=KGlobalSettings::alternateBackgroundColor();
00737 
00738   return QListView::event(e);
00739 }
00740 
00741 void KListView::contentsMousePressEvent( QMouseEvent *e )
00742 {
00743   if( (selectionModeExt() == Extended) && (e->state() & ShiftButton) && !(e->state() & ControlButton) )
00744   {
00745     bool block = signalsBlocked();
00746     blockSignals( true );
00747 
00748     clearSelection();
00749 
00750     blockSignals( block );
00751   }
00752   else if ((selectionModeExt()==FileManager) && (d->selectedBySimpleMove))
00753   {
00754      d->selectedBySimpleMove=false;
00755      d->selectedUsingMouse=true;
00756      if (currentItem()!=0)
00757      {
00758         currentItem()->setSelected(false);
00759         currentItem()->repaint();
00760 //        emit selectionChanged();
00761      };
00762   };
00763 
00764   QPoint p( contentsToViewport( e->pos() ) );
00765   QListViewItem *at = itemAt (p);
00766 
00767   // true if the root decoration of the item "at" was clicked (i.e. the +/- sign)
00768   bool rootDecoClicked = at
00769            && ( p.x() <= header()->cellPos( header()->mapToActual( 0 ) ) +
00770                 treeStepSize() * ( at->depth() + ( rootIsDecorated() ? 1 : 0) ) + itemMargin() )
00771            && ( p.x() >= header()->cellPos( header()->mapToActual( 0 ) ) );
00772 
00773   if (e->button() == LeftButton && !rootDecoClicked)
00774   {
00775     //Start a drag
00776     d->startDragPos = e->pos();
00777 
00778     if (at)
00779     {
00780       d->validDrag = true;
00781       d->pressedOnSelected = at->isSelected();
00782     }
00783   }
00784 
00785   QListView::contentsMousePressEvent( e );
00786 }
00787 
00788 void KListView::contentsMouseMoveEvent( QMouseEvent *e )
00789 {
00790   if (!dragEnabled() || d->startDragPos.isNull() || !d->validDrag)
00791       QListView::contentsMouseMoveEvent (e);
00792 
00793   QPoint vp = contentsToViewport(e->pos());
00794   QListViewItem *item = itemAt( vp );
00795 
00796   //do we process cursor changes at all?
00797   if ( item && d->bChangeCursorOverItem && d->bUseSingle )
00798     {
00799       //Cursor moved on a new item or in/out the execute area
00800       if( (item != d->pCurrentItem) ||
00801           (isExecuteArea(vp) != d->cursorInExecuteArea) )
00802         {
00803           d->cursorInExecuteArea = isExecuteArea(vp);
00804 
00805           if( d->cursorInExecuteArea ) //cursor moved in execute area
00806             viewport()->setCursor( KCursor::handCursor() );
00807           else //cursor moved out of execute area
00808             viewport()->unsetCursor();
00809         }
00810     }
00811 
00812   bool dragOn = dragEnabled();
00813   QPoint newPos = e->pos();
00814   if (dragOn && d->validDrag &&
00815       (newPos.x() > d->startDragPos.x()+d->dragDelay ||
00816        newPos.x() < d->startDragPos.x()-d->dragDelay ||
00817        newPos.y() > d->startDragPos.y()+d->dragDelay ||
00818        newPos.y() < d->startDragPos.y()-d->dragDelay))
00819     //(d->startDragPos - e->pos()).manhattanLength() > QApplication::startDragDistance())
00820     {
00821       QListView::contentsMouseReleaseEvent( 0 );
00822       startDrag();
00823       d->startDragPos = QPoint();
00824       d->validDrag = false;
00825     }
00826 }
00827 
00828 void KListView::contentsMouseReleaseEvent( QMouseEvent *e )
00829 {
00830   if (e->button() == LeftButton)
00831   {
00832     // If the row was already selected, maybe we want to start an in-place editing
00833     if ( d->pressedOnSelected && itemsRenameable() )
00834     {
00835       QPoint p( contentsToViewport( e->pos() ) );
00836       QListViewItem *at = itemAt (p);
00837       if ( at )
00838       {
00839         // true if the root decoration of the item "at" was clicked (i.e. the +/- sign)
00840         bool rootDecoClicked =
00841                   ( p.x() <= header()->cellPos( header()->mapToActual( 0 ) ) +
00842                     treeStepSize() * ( at->depth() + ( rootIsDecorated() ? 1 : 0) ) + itemMargin() )
00843                && ( p.x() >= header()->cellPos( header()->mapToActual( 0 ) ) );
00844 
00845         if (!rootDecoClicked)
00846         {
00847           int col = header()->mapToLogical( header()->cellAt( p.x() ) );
00848           if ( d->renameable.contains(col) )
00849             rename(at, col);
00850         }
00851       }
00852     }
00853 
00854     d->pressedOnSelected = false;
00855     d->validDrag = false;
00856     d->startDragPos = QPoint();
00857   }
00858   QListView::contentsMouseReleaseEvent( e );
00859 }
00860 
00861 void KListView::contentsMouseDoubleClickEvent ( QMouseEvent *e )
00862 {
00863   // We don't want to call the parent method because it does setOpen,
00864   // whereas we don't do it in single click mode... (David)
00865   //QListView::contentsMouseDoubleClickEvent( e );
00866 
00867   QPoint vp = contentsToViewport(e->pos());
00868   QListViewItem *item = itemAt( vp );
00869   emit QListView::doubleClicked( item ); // we do it now
00870 
00871   int col = item ? header()->mapToLogical( header()->cellAt( vp.x() ) ) : -1;
00872 
00873   if( item ) {
00874     emit doubleClicked( item, e->globalPos(), col );
00875 
00876     if( (e->button() == LeftButton) && !d->bUseSingle )
00877       emitExecute( item, e->globalPos(), col );
00878   }
00879 }
00880 
00881 void KListView::slotMouseButtonClicked( int btn, QListViewItem *item, const QPoint &pos, int c )
00882 {
00883   if( (btn == LeftButton) && item )
00884     emitExecute(item, pos, c);
00885 }
00886 
00887 void KListView::contentsDropEvent(QDropEvent* e)
00888 {
00889   cleanDropVisualizer();
00890   cleanItemHighlighter();
00891   d->dragExpand.stop();
00892 
00893   if (acceptDrag (e))
00894   {
00895     e->acceptAction();
00896     QListViewItem *afterme;
00897     QListViewItem *parent;
00898     findDrop(e->pos(), parent, afterme);
00899 
00900     if (e->source() == viewport() && itemsMovable())
00901         movableDropEvent(parent, afterme);
00902     else
00903     {
00904         emit dropped(e, afterme);
00905         emit dropped(this, e, afterme);
00906         emit dropped(e, parent, afterme);
00907         emit dropped(this, e, parent, afterme);
00908     }
00909   }
00910 }
00911 
00912 void KListView::movableDropEvent (QListViewItem* parent, QListViewItem* afterme)
00913 {
00914   QPtrList<QListViewItem> items, afterFirsts, afterNows;
00915   QListViewItem *current=currentItem();
00916   bool hasMoved=false;
00917   for (QListViewItem *i = firstChild(), *iNext=0; i != 0; i = iNext)
00918   {
00919     iNext=i->itemBelow();
00920     if (!i->isSelected())
00921       continue;
00922 
00923     // don't drop an item after itself, or else
00924     // it moves to the top of the list
00925     if (i==afterme)
00926       continue;
00927 
00928     i->setSelected(false);
00929 
00930     QListViewItem *afterFirst = i->itemAbove();
00931 
00932         if (!hasMoved)
00933         {
00934                 emit aboutToMove();
00935                 hasMoved=true;
00936         }
00937 
00938     moveItem(i, parent, afterme);
00939 
00940     // ###### This should include the new parent !!! -> KDE 3.0
00941     // If you need this right now, have a look at keditbookmarks.
00942     emit moved(i, afterFirst, afterme);
00943 
00944     items.append (i);
00945     afterFirsts.append (afterFirst);
00946     afterNows.append (afterme);
00947 
00948     afterme = i;
00949   }
00950   clearSelection();
00951   for (QListViewItem *i=items.first(); i != 0; i=items.next() )
00952     i->setSelected(true);
00953   if (current)
00954     setCurrentItem(current);
00955 
00956   emit moved(items,afterFirsts,afterNows);
00957 
00958   if (firstChild())
00959     emit moved();
00960 }
00961 
00962 void KListView::contentsDragMoveEvent(QDragMoveEvent *event)
00963 {
00964   if (acceptDrag(event))
00965   {
00966     event->acceptAction();
00967     //Clean up the view
00968 
00969     findDrop(event->pos(), d->parentItemDrop, d->afterItemDrop);
00970     QPoint vp = contentsToViewport( event->pos() );
00971     QListViewItem *item = isExecuteArea( vp ) ? itemAt( vp ) : 0L;
00972 
00973     if ( item != d->dragOverItem )
00974     {
00975       d->dragExpand.stop();
00976       d->dragOverItem = item;
00977       d->dragOverPoint = vp;
00978       if ( d->dragOverItem && d->dragOverItem->isExpandable() && !d->dragOverItem->isOpen() )
00979         d->dragExpand.start( QApplication::startDragTime(), true );
00980     }
00981     if (dropVisualizer())
00982     {
00983       QRect tmpRect = drawDropVisualizer(0, d->parentItemDrop, d->afterItemDrop);
00984       if (tmpRect != d->mOldDropVisualizer)
00985       {
00986         cleanDropVisualizer();
00987         d->mOldDropVisualizer=tmpRect;
00988         viewport()->repaint(tmpRect);
00989       }
00990     }
00991     if (dropHighlighter())
00992     {
00993       QRect tmpRect = drawItemHighlighter(0, d->afterItemDrop);
00994       if (tmpRect != d->mOldDropHighlighter)
00995       {
00996         cleanItemHighlighter();
00997         d->mOldDropHighlighter=tmpRect;
00998         viewport()->repaint(tmpRect);
00999       }
01000     }
01001   }
01002   else
01003       event->ignore();
01004 }
01005 
01006 void KListView::slotDragExpand()
01007 {
01008   if ( itemAt( d->dragOverPoint ) == d->dragOverItem )
01009     d->dragOverItem->setOpen( true );
01010 }
01011 
01012 void KListView::contentsDragLeaveEvent (QDragLeaveEvent*)
01013 {
01014   d->dragExpand.stop();
01015   cleanDropVisualizer();
01016   cleanItemHighlighter();
01017 }
01018 
01019 void KListView::cleanDropVisualizer()
01020 {
01021   if (d->mOldDropVisualizer.isValid())
01022   {
01023     QRect rect=d->mOldDropVisualizer;
01024     d->mOldDropVisualizer = QRect();
01025     viewport()->repaint(rect, true);
01026   }
01027 }
01028 
01029 int KListView::depthToPixels( int depth )
01030 {
01031     return treeStepSize() * ( depth + (rootIsDecorated() ? 1 : 0) ) + itemMargin();
01032 }
01033 
01034 void KListView::findDrop(const QPoint &pos, QListViewItem *&parent, QListViewItem *&after)
01035 {
01036     QPoint p (contentsToViewport(pos));
01037 
01038     // Get the position to put it in
01039     QListViewItem *atpos = itemAt(p);
01040 
01041     QListViewItem *above;
01042     if (!atpos) // put it at the end
01043         above = lastItem();
01044     else
01045     {
01046         // Get the closest item before us ('atpos' or the one above, if any)
01047         if (p.y() - itemRect(atpos).topLeft().y() < (atpos->height()/2))
01048             above = atpos->itemAbove();
01049         else
01050             above = atpos;
01051     }
01052 
01053     if (above)
01054     {
01055         // if above has children, I might need to drop it as the first item there
01056 
01057         if (above->firstChild() && above->isOpen())
01058         {
01059             parent = above;
01060             after = 0;
01061             return;
01062         }
01063 
01064       // Now, we know we want to go after "above". But as a child or as a sibling ?
01065       // We have to ask the "above" item if it accepts children.
01066       if (above->isExpandable())
01067       {
01068           // The mouse is sufficiently on the right ? - doesn't matter if 'above' has visible children
01069           if (p.x() >= depthToPixels( above->depth() + 1 ) ||
01070               (above->isOpen() && above->childCount() > 0) )
01071           {
01072               parent = above;
01073               after = 0L;
01074               return;
01075           }
01076       }
01077 
01078       // Ok, there's one more level of complexity. We may want to become a new
01079       // sibling, but of an upper-level group, rather than the "above" item
01080       QListViewItem * betterAbove = above->parent();
01081       QListViewItem * last = above;
01082       while ( betterAbove )
01083       {
01084           // We are allowed to become a sibling of "betterAbove" only if we are
01085           // after its last child
01086           if ( last->nextSibling() == 0 )
01087           {
01088               if (p.x() < depthToPixels ( betterAbove->depth() + 1 ))
01089                   above = betterAbove; // store this one, but don't stop yet, there may be a better one
01090               else
01091                   break; // not enough on the left, so stop
01092               last = betterAbove;
01093               betterAbove = betterAbove->parent(); // up one level
01094           } else
01095               break; // we're among the child of betterAbove, not after the last one
01096       }
01097   }
01098   // set as sibling
01099   after = above;
01100   parent = after ? after->parent() : 0L ;
01101 }
01102 
01103 QListViewItem* KListView::lastChild () const
01104 {
01105   QListViewItem* lastchild = firstChild();
01106 
01107   if (lastchild)
01108         for (; lastchild->nextSibling(); lastchild = lastchild->nextSibling());
01109 
01110   return lastchild;
01111 }
01112 
01113 QListViewItem *KListView::lastItem() const
01114 {
01115   QListViewItem* last = lastChild();
01116 
01117   for (QListViewItemIterator it (last); it.current(); ++it)
01118     last = it.current();
01119 
01120   return last;
01121 }
01122 
01123 KLineEdit *KListView::renameLineEdit() const
01124 {
01125   return d->editor;
01126 }
01127 
01128 void KListView::startDrag()
01129 {
01130   QDragObject *drag = dragObject();
01131 
01132   if (!drag)
01133         return;
01134 
01135   if (drag->drag() && drag->target() != viewport())
01136     emit moved();
01137 }
01138 
01139 QDragObject *KListView::dragObject()
01140 {
01141   if (!currentItem())
01142         return 0;
01143 
01144   return new QStoredDrag("application/x-qlistviewitem", viewport());
01145 }
01146 
01147 void KListView::setItemsMovable(bool b)
01148 {
01149   d->itemsMovable=b;
01150 }
01151 
01152 bool KListView::itemsMovable() const
01153 {
01154   return d->itemsMovable;
01155 }
01156 
01157 void KListView::setItemsRenameable(bool b)
01158 {
01159   d->itemsRenameable=b;
01160 }
01161 
01162 bool KListView::itemsRenameable() const
01163 {
01164   return d->itemsRenameable;
01165 }
01166 
01167 
01168 void KListView::setDragEnabled(bool b)
01169 {
01170   d->dragEnabled=b;
01171 }
01172 
01173 bool KListView::dragEnabled() const
01174 {
01175   return d->dragEnabled;
01176 }
01177 
01178 void KListView::setAutoOpen(bool b)
01179 {
01180   d->autoOpen=b;
01181 }
01182 
01183 bool KListView::autoOpen() const
01184 {
01185   return d->autoOpen;
01186 }
01187 
01188 bool KListView::dropVisualizer() const
01189 {
01190   return d->dropVisualizer;
01191 }
01192 
01193 void KListView::setDropVisualizer(bool b)
01194 {
01195   d->dropVisualizer=b;
01196 }
01197 
01198 QPtrList<QListViewItem> KListView::selectedItems() const
01199 {
01200   QPtrList<QListViewItem> list;
01201 
01202   QListViewItemIterator it(const_cast<KListView *>(this), QListViewItemIterator::Selected);
01203 
01204   for(; it.current(); ++it)
01205       list.append(it.current());
01206 
01207   return list;
01208 }
01209 
01210 
01211 void KListView::moveItem(QListViewItem *item, QListViewItem *parent, QListViewItem *after)
01212 {
01213   // sanity check - don't move a item into its own child structure
01214   QListViewItem *i = parent;
01215   while(i)
01216     {
01217       if(i == item)
01218         return;
01219       i = i->parent();
01220     }
01221 
01222   if (after)
01223   {
01224       item->moveItem(after);
01225       return;
01226   }
01227 
01228   // NOTE: This code shouldn't ever be reached if this method is used proprely,
01229   // QListVIew::moveItem() handles the same cases.  However, to avoid changing the (albeit
01230   // undocumented behavior) it's being left in for the moment.
01231 
01232   // Basically reimplementing the QListViewItem(QListViewItem*, QListViewItem*) constructor
01233   // in here, without ever deleting the item.
01234   if (item->parent())
01235         item->parent()->takeItem(item);
01236   else
01237         takeItem(item);
01238 
01239   if (parent)
01240         parent->insertItem(item);
01241   else
01242         insertItem(item);
01243 }
01244 
01245 void KListView::contentsDragEnterEvent(QDragEnterEvent *event)
01246 {
01247   if (acceptDrag (event))
01248     event->accept();
01249 }
01250 
01251 void KListView::setDropVisualizerWidth (int w)
01252 {
01253   d->mDropVisualizerWidth = w > 0 ? w : 1;
01254 }
01255 
01256 QRect KListView::drawDropVisualizer(QPainter *p, QListViewItem *parent,
01257                                     QListViewItem *after)
01258 {
01259     QRect insertmarker;
01260 
01261     if (!after && !parent)
01262         insertmarker = QRect (0, 0, viewport()->width(), d->mDropVisualizerWidth/2);
01263     else
01264     {
01265         int level = 0;
01266         if (after)
01267         {
01268             QListViewItem* it = 0L;
01269             if (after->isOpen())
01270             {
01271                 // Look for the last child (recursively)
01272                 it = after->firstChild();
01273                 if (it)
01274                     while (it->nextSibling() || it->firstChild())
01275                         if ( it->nextSibling() )
01276                             it = it->nextSibling();
01277                         else
01278                             it = it->firstChild();
01279             }
01280 
01281             insertmarker = itemRect (it ? it : after);
01282             level = after->depth();
01283         }
01284         else if (parent)
01285         {
01286             insertmarker = itemRect (parent);
01287             level = parent->depth() + 1;
01288         }
01289         insertmarker.setLeft( treeStepSize() * ( level + (rootIsDecorated() ? 1 : 0) ) + itemMargin() );
01290         insertmarker.setRight (viewport()->width());
01291         insertmarker.setTop (insertmarker.bottom() - d->mDropVisualizerWidth/2 + 1);
01292         insertmarker.setBottom (insertmarker.bottom() + d->mDropVisualizerWidth/2);
01293     }
01294 
01295     // This is not used anymore, at least by KListView itself (see viewportPaintEvent)
01296     // Remove for KDE 3.0.
01297     if (p)
01298         p->fillRect(insertmarker, Dense4Pattern);
01299 
01300     return insertmarker;
01301 }
01302 
01303 QRect KListView::drawItemHighlighter(QPainter *painter, QListViewItem *item)
01304 {
01305   QRect r;
01306 
01307   if (item)
01308   {
01309     r = itemRect(item);
01310     r.setLeft(r.left()+(item->depth()+1)*treeStepSize());
01311     if (painter)
01312       style().drawPrimitive(QStyle::PE_FocusRect, painter, r, colorGroup(),
01313                             QStyle::Style_FocusAtBorder, colorGroup().highlight());
01314   }
01315 
01316   return r;
01317 }
01318 
01319 void KListView::cleanItemHighlighter ()
01320 {
01321   if (d->mOldDropHighlighter.isValid())
01322   {
01323     QRect rect=d->mOldDropHighlighter;
01324     d->mOldDropHighlighter = QRect();
01325     viewport()->repaint(rect, true);
01326   }
01327 }
01328 
01329 void KListView::rename(QListViewItem *item, int c)
01330 {
01331   if (d->renameable.contains(c))
01332   {
01333     ensureItemVisible(item);
01334     d->editor->load(item,c);
01335   }
01336 }
01337 
01338 bool KListView::isRenameable (int col) const
01339 {
01340   return d->renameable.contains(col);
01341 }
01342 
01343 void KListView::setRenameable (int col, bool yesno)
01344 {
01345   if (col>=header()->count()) return;
01346 
01347   d->renameable.remove(col);
01348   if (yesno && d->renameable.find(col)==d->renameable.end())
01349     d->renameable+=col;
01350   else if (!yesno && d->renameable.find(col)!=d->renameable.end())
01351     d->renameable.remove(col);
01352 }
01353 
01354 void KListView::doneEditing(QListViewItem *item, int row)
01355 {
01356   emit itemRenamed(item, item->text(row), row);
01357   emit itemRenamed(item);
01358 }
01359 
01360 bool KListView::acceptDrag(QDropEvent* e) const
01361 {
01362   return acceptDrops() && itemsMovable() && (e->source()==viewport());
01363 }
01364 
01365 void KListView::setCreateChildren(bool b)
01366 {
01367         d->createChildren=b;
01368 }
01369 
01370 bool KListView::createChildren() const
01371 {
01372         return d->createChildren;
01373 }
01374 
01375 
01376 int KListView::tooltipColumn() const
01377 {
01378         return d->tooltipColumn;
01379 }
01380 
01381 void KListView::setTooltipColumn(int column)
01382 {
01383         d->tooltipColumn=column;
01384 }
01385 
01386 void KListView::setDropHighlighter(bool b)
01387 {
01388         d->dropHighlighter=b;
01389 }
01390 
01391 bool KListView::dropHighlighter() const
01392 {
01393         return d->dropHighlighter;
01394 }
01395 
01396 bool KListView::showTooltip(QListViewItem *item, const QPoint &, int column) const
01397 {
01398         return ((tooltip(item, column).length()>0) && (column==tooltipColumn()));
01399 }
01400 
01401 QString KListView::tooltip(QListViewItem *item, int column) const
01402 {
01403         return item->text(column);
01404 }
01405 
01406 void KListView::setTabOrderedRenaming(bool b)
01407 {
01408     d->tabRename = b;
01409 }
01410 
01411 bool KListView::tabOrderedRenaming() const
01412 {
01413     return d->tabRename;
01414 }
01415 
01416 void KListView::keyPressEvent (QKeyEvent* e)
01417 {
01418   //don't we need a contextMenuModifier too ? (aleXXX)
01419   if (e->key() == d->contextMenuKey)
01420         {
01421           emit menuShortCutPressed (this, currentItem());
01422           return;
01423         }
01424 
01425   if (d->selectionMode != FileManager)
01426         QListView::keyPressEvent (e);
01427   else
01428         fileManagerKeyPressEvent (e);
01429 }
01430 
01431 void KListView::activateAutomaticSelection()
01432 {
01433    d->selectedBySimpleMove=true;
01434    d->selectedUsingMouse=false;
01435    if (currentItem()!=0)
01436    {
01437       selectAll(false);
01438       currentItem()->setSelected(true);
01439       currentItem()->repaint();
01440       emit selectionChanged();
01441    };
01442 }
01443 
01444 void KListView::deactivateAutomaticSelection()
01445 {
01446    d->selectedBySimpleMove=false;
01447 }
01448 
01449 bool KListView::automaticSelection() const
01450 {
01451    return d->selectedBySimpleMove;
01452 }
01453 
01454 void KListView::fileManagerKeyPressEvent (QKeyEvent* e)
01455 {
01456    //don't care whether it's on the keypad or not
01457     int e_state=(e->state() & ~Keypad);
01458 
01459     int oldSelectionDirection(d->selectionDirection);
01460 
01461     if ((e->key()!=Key_Shift) && (e->key()!=Key_Control)
01462         && (e->key()!=Key_Meta) && (e->key()!=Key_Alt))
01463     {
01464        if ((e_state==ShiftButton) && (!d->wasShiftEvent) && (!d->selectedBySimpleMove))
01465           selectAll(false);
01466        d->selectionDirection=0;
01467        d->wasShiftEvent = (e_state == ShiftButton);
01468     };
01469 
01470     //d->wasShiftEvent = (e_state == ShiftButton);
01471 
01472 
01473     QListViewItem* item = currentItem();
01474     if (item==0) return;
01475 
01476     QListViewItem* repaintItem1 = item;
01477     QListViewItem* repaintItem2 = 0L;
01478     QListViewItem* visItem = 0L;
01479 
01480     QListViewItem* nextItem = 0L;
01481     int items = 0;
01482 
01483     bool shiftOrCtrl((e_state==ControlButton) || (e_state==ShiftButton));
01484     int selectedItems(0);
01485     for (QListViewItem *tmpItem=firstChild(); tmpItem!=0; tmpItem=tmpItem->nextSibling())
01486        if (tmpItem->isSelected()) selectedItems++;
01487 
01488     if (((selectedItems==0) || ((selectedItems==1) && (d->selectedUsingMouse)))
01489         && (e_state==NoButton)
01490         && ((e->key()==Key_Down)
01491         || (e->key()==Key_Up)
01492         || (e->key()==Key_Next)
01493         || (e->key()==Key_Prior)
01494         || (e->key()==Key_Home)
01495         || (e->key()==Key_End)))
01496     {
01497        d->selectedBySimpleMove=true;
01498        d->selectedUsingMouse=false;
01499     }
01500     else if (selectedItems>1)
01501        d->selectedBySimpleMove=false;
01502 
01503     bool emitSelectionChanged(false);
01504 
01505     switch (e->key())
01506     {
01507     case Key_Escape:
01508        selectAll(false);
01509        emitSelectionChanged=true;
01510        break;
01511 
01512     case Key_Space:
01513        //toggle selection of current item
01514        if (d->selectedBySimpleMove)
01515           d->selectedBySimpleMove=false;
01516        item->setSelected(!item->isSelected());
01517        emitSelectionChanged=true;
01518        break;
01519 
01520     case Key_Insert:
01521        //toggle selection of current item and move to the next item
01522        if (d->selectedBySimpleMove)
01523        {
01524           d->selectedBySimpleMove=false;
01525           if (!item->isSelected()) item->setSelected(true);
01526        }
01527        else
01528        {
01529           item->setSelected(!item->isSelected());
01530        };
01531 
01532        nextItem=item->itemBelow();
01533 
01534        if (nextItem!=0)
01535        {
01536           repaintItem2=nextItem;
01537           visItem=nextItem;
01538           setCurrentItem(nextItem);
01539        };
01540        d->selectionDirection=1;
01541        emitSelectionChanged=true;
01542        break;
01543 
01544     case Key_Down:
01545        nextItem=item->itemBelow();
01546        //toggle selection of current item and move to the next item
01547        if (shiftOrCtrl)
01548        {
01549           d->selectionDirection=1;
01550           if (d->selectedBySimpleMove)
01551              d->selectedBySimpleMove=false;
01552           else
01553           {
01554              if (oldSelectionDirection!=-1)
01555              {
01556                 item->setSelected(!item->isSelected());
01557                 emitSelectionChanged=true;
01558              };
01559           };
01560        }
01561        else if ((d->selectedBySimpleMove) && (nextItem!=0))
01562        {
01563           item->setSelected(false);
01564           emitSelectionChanged=true;
01565        };
01566 
01567        if (nextItem!=0)
01568        {
01569           if (d->selectedBySimpleMove)
01570              nextItem->setSelected(true);
01571           repaintItem2=nextItem;
01572           visItem=nextItem;
01573           setCurrentItem(nextItem);
01574        };
01575        break;
01576 
01577     case Key_Up:
01578        nextItem=item->itemAbove();
01579        d->selectionDirection=-1;
01580        //move to the prev. item and toggle selection of this one
01581        // => No, can't select the last item, with this. For symmetry, let's
01582        // toggle selection and THEN move up, just like we do in down (David)
01583        if (shiftOrCtrl)
01584        {
01585           if (d->selectedBySimpleMove)
01586              d->selectedBySimpleMove=false;
01587           else
01588           {
01589              if (oldSelectionDirection!=1)
01590              {
01591                 item->setSelected(!item->isSelected());
01592                 emitSelectionChanged=true;
01593              };
01594           }
01595        }
01596        else if ((d->selectedBySimpleMove) && (nextItem!=0))
01597        {
01598           item->setSelected(false);
01599           emitSelectionChanged=true;
01600        };
01601 
01602        if (nextItem!=0)
01603        {
01604           if (d->selectedBySimpleMove)
01605              nextItem->setSelected(true);
01606           repaintItem2=nextItem;
01607           visItem=nextItem;
01608           setCurrentItem(nextItem);
01609        };
01610        break;
01611 
01612     case Key_End:
01613        //move to the last item and toggle selection of all items inbetween
01614        nextItem=item;
01615        if (d->selectedBySimpleMove)
01616           item->setSelected(false);
01617        if (shiftOrCtrl)
01618           d->selectedBySimpleMove=false;
01619 
01620        while(nextItem!=0)
01621        {
01622           if (shiftOrCtrl)
01623              nextItem->setSelected(!nextItem->isSelected());
01624           if (nextItem->itemBelow()==0)
01625           {
01626              if (d->selectedBySimpleMove)
01627                 nextItem->setSelected(true);
01628              repaintItem2=nextItem;
01629              visItem=nextItem;
01630              setCurrentItem(nextItem);
01631           }
01632           nextItem=nextItem->itemBelow();
01633        }
01634        emitSelectionChanged=true;
01635        break;
01636 
01637     case Key_Home:
01638        // move to the first item and toggle selection of all items inbetween
01639        nextItem = firstChild();
01640        visItem = nextItem;
01641        repaintItem2 = visItem;
01642        if (d->selectedBySimpleMove)
01643           item->setSelected(false);
01644        if (shiftOrCtrl)
01645        {
01646           d->selectedBySimpleMove=false;
01647 
01648           while ( nextItem != item )
01649           {
01650              nextItem->setSelected( !nextItem->isSelected() );
01651              nextItem = nextItem->itemBelow();
01652           }
01653           item->setSelected( !item->isSelected() );
01654        }
01655        setCurrentItem( firstChild() );
01656        emitSelectionChanged=true;
01657        break;
01658 
01659     case Key_Next:
01660        items=visibleHeight()/item->height();
01661        nextItem=item;
01662        if (d->selectedBySimpleMove)
01663           item->setSelected(false);
01664        if (shiftOrCtrl)
01665        {
01666           d->selectedBySimpleMove=false;
01667           d->selectionDirection=1;
01668        };
01669 
01670        for (int i=0; i<items; i++)
01671        {
01672           if (shiftOrCtrl)
01673              nextItem->setSelected(!nextItem->isSelected());
01674           //the end
01675           if ((i==items-1) || (nextItem->itemBelow()==0))
01676 
01677           {
01678              if (shiftOrCtrl)
01679                 nextItem->setSelected(!nextItem->isSelected());
01680              if (d->selectedBySimpleMove)
01681                 nextItem->setSelected(true);
01682              ensureItemVisible(nextItem);
01683              setCurrentItem(nextItem);
01684              update();
01685              if ((shiftOrCtrl) || (d->selectedBySimpleMove))
01686              {
01687                 emit selectionChanged();
01688              }
01689              return;
01690           }
01691           nextItem=nextItem->itemBelow();
01692        }
01693        break;
01694 
01695     case Key_Prior:
01696        items=visibleHeight()/item->height();
01697        nextItem=item;
01698        if (d->selectedBySimpleMove)
01699           item->setSelected(false);
01700        if (shiftOrCtrl)
01701        {
01702           d->selectionDirection=-1;
01703           d->selectedBySimpleMove=false;
01704        };
01705 
01706        for (int i=0; i<items; i++)
01707        {
01708           if ((nextItem!=item) &&(shiftOrCtrl))
01709              nextItem->setSelected(!nextItem->isSelected());
01710           //the end
01711           if ((i==items-1) || (nextItem->itemAbove()==0))
01712 
01713           {
01714              if (d->selectedBySimpleMove)
01715                 nextItem->setSelected(true);
01716              ensureItemVisible(nextItem);
01717              setCurrentItem(nextItem);
01718              update();
01719              if ((shiftOrCtrl) || (d->selectedBySimpleMove))
01720              {
01721                 emit selectionChanged();
01722              }
01723              return;
01724           }
01725           nextItem=nextItem->itemAbove();
01726        }
01727        break;
01728 
01729     case Key_Minus:
01730        if ( item->isOpen() )
01731           setOpen( item, false );
01732        break;
01733     case Key_Plus:
01734        if (  !item->isOpen() && (item->isExpandable() || item->childCount()) )
01735           setOpen( item, true );
01736        break;
01737     default:
01738        bool realKey = ((e->key()!=Key_Shift) && (e->key()!=Key_Control)
01739                         && (e->key()!=Key_Meta) && (e->key()!=Key_Alt));
01740 
01741        bool selectCurrentItem = (d->selectedBySimpleMove) && (item->isSelected());
01742        if (realKey && selectCurrentItem)
01743           item->setSelected(false);
01744        //this is mainly for the "goto filename beginning with pressed char" feature (aleXXX)
01745        QListView::SelectionMode oldSelectionMode = selectionMode();
01746        setSelectionMode (QListView::Multi);
01747        QListView::keyPressEvent (e);
01748        setSelectionMode (oldSelectionMode);
01749        if (realKey && selectCurrentItem)
01750        {
01751           currentItem()->setSelected(true);
01752           emitSelectionChanged=true;
01753        }
01754        repaintItem2=currentItem();
01755        if (realKey)
01756           visItem=currentItem();
01757        break;
01758     }
01759 
01760     if (visItem)
01761        ensureItemVisible(visItem);
01762 
01763     QRect ir;
01764     if (repaintItem1)
01765        ir = ir.unite( itemRect(repaintItem1) );
01766     if (repaintItem2)
01767        ir = ir.unite( itemRect(repaintItem2) );
01768 
01769     if ( !ir.isEmpty() )
01770     {                 // rectangle to be repainted
01771        if ( ir.x() < 0 )
01772           ir.moveBy( -ir.x(), 0 );
01773        viewport()->repaint( ir, false );
01774     }
01775     /*if (repaintItem1)
01776        repaintItem1->repaint();
01777     if (repaintItem2)
01778        repaintItem2->repaint();*/
01779     update();
01780     if (emitSelectionChanged)
01781        emit selectionChanged();
01782 }
01783 
01784 void KListView::setSelectionModeExt (SelectionModeExt mode)
01785 {
01786     d->selectionMode = mode;
01787 
01788     switch (mode)
01789     {
01790     case Single:
01791     case Multi:
01792     case Extended:
01793     case NoSelection:
01794         setSelectionMode (static_cast<QListView::SelectionMode>(static_cast<int>(mode)));
01795         break;
01796 
01797     case FileManager:
01798         setSelectionMode (QListView::Extended);
01799         break;
01800 
01801     default:
01802         kdWarning () << "Warning: illegal selection mode " << int(mode) << " set!" << endl;
01803         break;
01804     }
01805 }
01806 
01807 KListView::SelectionModeExt KListView::selectionModeExt () const
01808 {
01809   return d->selectionMode;
01810 }
01811 
01812 int KListView::itemIndex( const QListViewItem *item ) const
01813 {
01814     if ( !item )
01815         return -1;
01816 
01817     if ( item == firstChild() )
01818         return 0;
01819     else {
01820         QListViewItemIterator it(firstChild());
01821         uint j = 0;
01822         for (; it.current() && it.current() != item; ++it, ++j );
01823 
01824         if( !it.current() )
01825           return -1;
01826 
01827         return j;
01828     }
01829 }
01830 
01831 QListViewItem* KListView::itemAtIndex(int index)
01832 {
01833    if (index<0)
01834       return 0;
01835 
01836    int j(0);
01837    for (QListViewItemIterator it=firstChild(); it.current(); it++)
01838    {
01839       if (j==index)
01840          return it.current();
01841       j++;
01842    };
01843    return 0;
01844 }
01845 
01846 
01847 void KListView::emitContextMenu (KListView*, QListViewItem* i)
01848 {
01849   QPoint p;
01850 
01851   if (i)
01852         p = viewport()->mapToGlobal(itemRect(i).center());
01853   else
01854         p = mapToGlobal(rect().center());
01855 
01856   emit contextMenu (this, i, p);
01857 }
01858 
01859 void KListView::emitContextMenu (QListViewItem* i, const QPoint& p, int)
01860 {
01861   emit contextMenu (this, i, p);
01862 }
01863 
01864 void KListView::setAcceptDrops (bool val)
01865 {
01866   QListView::setAcceptDrops (val);
01867   viewport()->setAcceptDrops (val);
01868 }
01869 
01870 int KListView::dropVisualizerWidth () const
01871 {
01872         return d->mDropVisualizerWidth;
01873 }
01874 
01875 
01876 void KListView::viewportPaintEvent(QPaintEvent *e)
01877 {
01878   d->paintAbove = 0;
01879   d->paintCurrent = 0;
01880   d->paintBelow = 0;
01881   d->painting = true;
01882 
01883   QListView::viewportPaintEvent(e);
01884 
01885   if (d->mOldDropVisualizer.isValid() && e->rect().intersects(d->mOldDropVisualizer))
01886     {
01887       QPainter painter(viewport());
01888 
01889       // This is where we actually draw the drop-visualizer
01890       painter.fillRect(d->mOldDropVisualizer, Dense4Pattern);
01891     }
01892   if (d->mOldDropHighlighter.isValid() && e->rect().intersects(d->mOldDropHighlighter))
01893     {
01894       QPainter painter(viewport());
01895 
01896       // This is where we actually draw the drop-highlighter
01897       style().drawPrimitive(QStyle::PE_FocusRect, &painter, d->mOldDropHighlighter, colorGroup(),
01898                             QStyle::Style_FocusAtBorder);
01899     }
01900   d->painting = false;
01901 }
01902 
01903 void KListView::setFullWidth()
01904 {
01905   setFullWidth(true);
01906 }
01907 
01908 void KListView::setFullWidth(bool fullWidth)
01909 {
01910   d->fullWidth = fullWidth;
01911   header()->setStretchEnabled(fullWidth, columns()-1);
01912 }
01913 
01914 bool KListView::fullWidth() const
01915 {
01916   return d->fullWidth;
01917 }
01918 
01919 int KListView::addColumn(const QString& label, int width)
01920 {
01921   int result = QListView::addColumn(label, width);
01922   if (d->fullWidth) {
01923     header()->setStretchEnabled(false, columns()-2);
01924     header()->setStretchEnabled(true, columns()-1);
01925   }
01926   return result;
01927 }
01928 
01929 int KListView::addColumn(const QIconSet& iconset, const QString& label, int width)
01930 {
01931   int result = QListView::addColumn(iconset, label, width);
01932   if (d->fullWidth) {
01933     header()->setStretchEnabled(false, columns()-2);
01934     header()->setStretchEnabled(true, columns()-1);
01935   }
01936   return result;
01937 }
01938 
01939 void KListView::removeColumn(int index)
01940 {
01941   QListView::removeColumn(index);
01942   if (d->fullWidth && index == columns()) header()->setStretchEnabled(true, columns()-1);
01943 }
01944 
01945 void KListView::viewportResizeEvent(QResizeEvent* e)
01946 {
01947   QListView::viewportResizeEvent(e);
01948 }
01949 
01950 const QColor &KListView::alternateBackground() const
01951 {
01952   return d->alternateBackground;
01953 }
01954 
01955 void KListView::setAlternateBackground(const QColor &c)
01956 {
01957   d->alternateBackground = c;
01958   repaint();
01959 }
01960 
01961 void KListView::saveLayout(KConfig *config, const QString &group) const
01962 {
01963   KConfigGroupSaver saver(config, group);
01964   QStringList widths, order;
01965   for (int i = 0; i < columns(); ++i)
01966   {
01967     widths << QString::number(columnWidth(i));
01968     order << QString::number(header()->mapToIndex(i));
01969   }
01970   config->writeEntry("ColumnWidths", widths);
01971   config->writeEntry("ColumnOrder", order);
01972   config->writeEntry("SortColumn", d->sortColumn);
01973   config->writeEntry("SortAscending", d->sortAscending);
01974 }
01975 
01976 void KListView::restoreLayout(KConfig *config, const QString &group)
01977 {
01978   KConfigGroupSaver saver(config, group);
01979   QStringList cols = config->readListEntry("ColumnWidths");
01980   int i = 0;
01981   for (QStringList::ConstIterator it = cols.begin(); it != cols.end(); ++it)
01982     setColumnWidth(i++, (*it).toInt());
01983 
01984   cols = config->readListEntry("ColumnOrder");
01985   i = 0;
01986   for (QStringList::ConstIterator it = cols.begin(); it != cols.end(); ++it)
01987     header()->moveSection(i++, (*it).toInt());
01988   if (config->hasKey("SortColumn"))
01989     setSorting(config->readNumEntry("SortColumn"), config->readBoolEntry("SortAscending", true));
01990 }
01991 
01992 void KListView::setSorting(int column, bool ascending)
01993 {
01994   d->sortColumn = column;
01995   d->sortAscending = ascending;
01996   QListView::setSorting(column, ascending);
01997 }
01998 
01999 int KListView::columnSorted(void) const
02000 {
02001   return d->sortColumn;
02002 }
02003 
02004 bool KListView::ascendingSort(void) const
02005 {
02006   return d->sortAscending;
02007 }
02008 
02009 void KListView::takeItem(QListViewItem *item)
02010 {
02011   if(item && item == d->editor->currentItem())
02012     d->editor->terminate();
02013 
02014   QListView::takeItem(item);
02015 }
02016 
02017 void KListView::disableAutoSelection()
02018 {
02019   if ( d->disableAutoSelection )
02020     return;
02021 
02022   d->disableAutoSelection = true;
02023   d->autoSelect.stop();
02024   d->autoSelectDelay = -1;
02025 }
02026 
02027 void KListView::resetAutoSelection()
02028 {
02029   if ( !d->disableAutoSelection )
02030     return;
02031 
02032   d->disableAutoSelection = false;
02033   d->autoSelectDelay = KGlobalSettings::autoSelectDelay();
02034 }
02035 
02036 
02037 
02038 KListViewItem::KListViewItem(QListView *parent)
02039   : QListViewItem(parent)
02040 {
02041   init();
02042 }
02043 
02044 KListViewItem::KListViewItem(QListViewItem *parent)
02045   : QListViewItem(parent)
02046 {
02047   init();
02048 }
02049 
02050 KListViewItem::KListViewItem(QListView *parent, QListViewItem *after)
02051   : QListViewItem(parent, after)
02052 {
02053   init();
02054 }
02055 
02056 KListViewItem::KListViewItem(QListViewItem *parent, QListViewItem *after)
02057   : QListViewItem(parent, after)
02058 {
02059   init();
02060 }
02061 
02062 KListViewItem::KListViewItem(QListView *parent,
02063     QString label1, QString label2, QString label3, QString label4,
02064     QString label5, QString label6, QString label7, QString label8)
02065   : QListViewItem(parent, label1, label2, label3, label4, label5, label6, label7, label8)
02066 {
02067   init();
02068 }
02069 
02070 KListViewItem::KListViewItem(QListViewItem *parent,
02071     QString label1, QString label2, QString label3, QString label4,
02072     QString label5, QString label6, QString label7, QString label8)
02073   : QListViewItem(parent, label1, label2, label3, label4, label5, label6, label7, label8)
02074 {
02075   init();
02076 }
02077 
02078 KListViewItem::KListViewItem(QListView *parent, QListViewItem *after,
02079     QString label1, QString label2, QString label3, QString label4,
02080     QString label5, QString label6, QString label7, QString label8)
02081   : QListViewItem(parent, after, label1, label2, label3, label4, label5, label6, label7, label8)
02082 {
02083   init();
02084 }
02085 
02086 KListViewItem::KListViewItem(QListViewItem *parent, QListViewItem *after,
02087     QString label1, QString label2, QString label3, QString label4,
02088     QString label5, QString label6, QString label7, QString label8)
02089   : QListViewItem(parent, after, label1, label2, label3, label4, label5, label6, label7, label8)
02090 {
02091   init();
02092 }
02093 
02094 KListViewItem::~KListViewItem()
02095 {
02096 }
02097 
02098 void KListViewItem::init()
02099 {
02100   m_odd = m_known = false;
02101   KListView *lv = static_cast<KListView *>(listView());
02102   setDragEnabled( dragEnabled() || lv->dragEnabled() );
02103 }
02104 
02105 const QColor &KListViewItem::backgroundColor()
02106 {
02107   if (isAlternate())
02108     return static_cast< KListView* >(listView())->alternateBackground();
02109   return listView()->viewport()->colorGroup().base();
02110 }
02111 
02112 bool KListViewItem::isAlternate()
02113 {
02114   KListView *lv = static_cast<KListView *>(listView());
02115   if (lv && lv->alternateBackground().isValid())
02116   {
02117     KListViewItem *above;
02118 
02119     // Ok, there's some weirdness here that requires explanation as this is a
02120     // speed hack.  itemAbove() is a O(n) operation (though this isn't
02121     // immediately clear) so we want to call it as infrequently as possible --
02122     // especially in the case of painting a cell.
02123     //
02124     // So, in the case that we *are* painting a cell:  (1) we're assuming that
02125     // said painting is happening top to bottem -- this assumption is present
02126     // elsewhere in the implementation of this class, (2) itemBelow() is fast --
02127     // roughly constant time.
02128     //
02129     // Given these assumptions we can do a mixture of caching and telling the
02130     // next item that the when that item is the current item that the now
02131     // current item will be the item above it.
02132     //
02133     // Ideally this will make checking to see if the item above the current item
02134     // is the alternate color a constant time operation rather than 0(n).
02135 
02136     if (lv->d->painting) {
02137       if (lv->d->paintCurrent != this)
02138       {
02139         lv->d->paintAbove = lv->d->paintBelow == this ? lv->d->paintCurrent : itemAbove();
02140         lv->d->paintCurrent = this;
02141         lv->d->paintBelow = itemBelow();
02142       }
02143 
02144       above = dynamic_cast<KListViewItem *>(lv->d->paintAbove);
02145     }
02146     else
02147     {
02148       above = dynamic_cast<KListViewItem *>(itemAbove());
02149     }
02150 
02151     m_known = above ? above->m_known : true;
02152     if (m_known)
02153     {
02154        m_odd = above ? !above->m_odd : false;
02155     }
02156     else
02157     {
02158        KListViewItem *item;
02159        bool previous = true;
02160        if (parent())
02161        {
02162           item = dynamic_cast<KListViewItem *>(parent());
02163           if (item)
02164              previous = item->m_odd;
02165           item = dynamic_cast<KListViewItem *>(parent()->firstChild());
02166        }
02167        else
02168        {
02169           item = dynamic_cast<KListViewItem *>(lv->firstChild());
02170        }
02171 
02172        while(item)
02173        {
02174           item->m_odd = previous = !previous;
02175           item->m_known = true;
02176           item = dynamic_cast<KListViewItem *>(item->nextSibling());
02177        }
02178     }
02179     return m_odd;
02180   }
02181   return false;
02182 }
02183 
02184 void KListViewItem::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int alignment)
02185 {
02186   QColorGroup _cg = cg;
02187   const QPixmap *pm = listView()->viewport()->backgroundPixmap();
02188   if (pm && !pm->isNull())
02189   {
02190         _cg.setBrush(QColorGroup::Base, QBrush(backgroundColor(), *pm));
02191         QPoint o = p->brushOrigin();
02192         p->setBrushOrigin( o.x()-listView()->contentsX(), o.y()-listView()->contentsY() );
02193   }
02194   else if (isAlternate())
02195        if (listView()->viewport()->backgroundMode()==Qt::FixedColor)
02196             _cg.setColor(QColorGroup::Background, static_cast< KListView* >(listView())->alternateBackground());
02197        else
02198         _cg.setColor(QColorGroup::Base, static_cast< KListView* >(listView())->alternateBackground());
02199 
02200   QListViewItem::paintCell(p, _cg, column, width, alignment);
02201 }
02202 
02203 void KListView::virtual_hook( int, void* )
02204 { /*BASE::virtual_hook( id, data );*/ }
02205 
02206 #include "klistview.moc"
02207 #include "klistviewlineedit.moc"
02208 
02209 // vim: noet
KDE Logo
This file is part of the documentation for kdeui Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Sep 30 05:16:50 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2003