/* ============================================================ * * This file is a part of digiKam project * http://www.digikam.org * * Date : 2005-04-24 * Description : icons view. * * Copyright (C) 2005 by Renchi Raju * Copyright (C) 2006-2007 by Gilles Caulier * * This program is free software; you can redistribute it * and/or modify it under the terms of the GNU General * Public License as published by the Free Software Foundation; * either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * ============================================================ */ #define RECT_EXTENSION 300 // C++ includes. #include // TQt includes. #include #include #include #include #include #include #include // KDE includes. #include #include // Local includes. #include "ddebug.h" #include "iconitem.h" #include "icongroupitem.h" #include "iconview.h" #include "iconview.moc" namespace Digikam { class IconViewPriv { public: IconViewPriv() { firstGroup = 0; lastGroup = 0; currItem = 0; anchorItem = 0; clearing = false; spacing = 10; rubber = 0; dragging = false; pressedMoved = false; firstContainer = 0; lastContainer = 0; showTips = false; toolTipItem = 0; toolTipTimer = 0; rearrangeTimer = 0; rearrangeTimerInterval = 0; storedVisibleItem = 0; needEmitSelectionChanged = false; } bool clearing; bool showTips; bool pressedMoved; bool dragging; bool needEmitSelectionChanged; // store for slotRearrange int spacing; TQPtrDict selectedItems; TQPtrDict prevSelectedItems; TQRect* rubber; TQPoint dragStartPos; TQTimer* rearrangeTimer; TQTimer* toolTipTimer; IconItem* toolTipItem; IconItem* currItem; IconItem* anchorItem; IconItem* storedVisibleItem; // store position for slotRearrange IconGroupItem* firstGroup; IconGroupItem* lastGroup; int rearrangeTimerInterval; struct ItemContainer { ItemContainer(ItemContainer *p, ItemContainer *n, const TQRect &r) : prev(p), next(n), rect(r) { if (prev) prev->next = this; if (next) next->prev = this; } ItemContainer *prev, *next; TQRect rect; TQValueList items; } *firstContainer, *lastContainer; struct SortableItem { IconGroupItem *group; }; }; IconView::IconView(TQWidget* parent, const char* name) : TQScrollView(parent, name, TQt::WStaticContents|TQt::WNoAutoErase) { viewport()->setBackgroundMode(TQt::NoBackground); viewport()->setFocusProxy(this); viewport()->setFocusPolicy(TQWidget::WheelFocus); viewport()->setMouseTracking(true); d = new IconViewPriv; d->rearrangeTimer = new TQTimer(this); d->toolTipTimer = new TQTimer(this); connect(d->rearrangeTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotRearrange())); connect(d->toolTipTimer, TQ_SIGNAL(timeout()), this, TQ_SLOT(slotToolTip())); setEnableToolTips(true); } IconView::~IconView() { clear(false); delete d->rearrangeTimer; delete d->toolTipTimer; delete d->rubber; delete d; } IconGroupItem* IconView::firstGroup() const { return d->firstGroup; } IconGroupItem* IconView::lastGroup() const { return d->lastGroup; } IconItem* IconView::firstItem() const { if (!d->firstGroup) return 0; return d->firstGroup->firstItem(); } IconItem* IconView::lastItem() const { if (!d->lastGroup) return 0; return d->lastGroup->lastItem(); } IconItem* IconView::currentItem() const { return d->currItem; } void IconView::setCurrentItem(IconItem* item) { d->currItem = item; d->anchorItem = d->currItem; if (d->currItem) { d->currItem->setSelected(true, true); ensureItemVisible(d->currItem); } } IconItem* IconView::findItem(const TQPoint& pos) { IconViewPriv::ItemContainer *c = d->firstContainer; for (; c; c = c->next) { if ( c->rect.contains(pos) ) { for (TQValueList::iterator it = c->items.begin(); it != c->items.end(); ++it) { IconItem* item = *it; if (item->rect().contains(pos)) return item; } } } return 0; } IconGroupItem* IconView::findGroup(const TQPoint& pos) { TQPoint p = viewportToContents(viewport()->mapFromGlobal(pos)); for (IconGroupItem* group = d->firstGroup; group; group = group->nextGroup()) { TQRect rect = group->rect(); int bottom; if (group == d->lastGroup) bottom = contentsHeight(); else bottom = group->nextGroup()->rect().top(); rect.setBottom(bottom); if ( rect.contains(p) ) { return group; } } return 0; } int IconView::count() const { int c = 0; for (IconGroupItem* group = d->firstGroup; group; group = group->nextGroup()) { c += group->count(); } return c; } int IconView::countSelected() const { int c = 0; for (IconGroupItem* group = d->firstGroup; group; group = group->nextGroup()) { for (IconItem *it = group->firstItem(); it; it = it->nextItem()) if (it->isSelected()) c++; } return c; } int IconView::groupCount() const { int c = 0; for (IconGroupItem* group = d->firstGroup; group; group = group->nextGroup()) { c++; } return c; } void IconView::clear(bool update) { d->clearing = true; d->toolTipItem = 0; d->toolTipTimer->stop(); slotToolTip(); deleteContainers(); d->selectedItems.clear(); IconGroupItem *group = d->firstGroup; while (group) { IconGroupItem *tmp = group->m_next; delete group; group = tmp; } d->firstGroup = 0; d->lastGroup = 0; d->currItem = 0; d->anchorItem = 0; viewport()->setUpdatesEnabled(false); resizeContents(0, 0); setContentsPos(0, 0); viewport()->setUpdatesEnabled(true); if (update) updateContents(); d->clearing = false; emit signalSelectionChanged(); } void IconView::clearSelection() { bool wasBlocked = signalsBlocked();; if (!wasBlocked) blockSignals(true); TQPtrDict selItems = d->selectedItems; TQPtrDictIterator it( selItems ); for ( ; it.current(); ++it ) it.current()->setSelected(false, false); d->selectedItems.clear(); if (!wasBlocked) blockSignals(false); emit signalSelectionChanged(); } void IconView::selectAll() { bool wasBlocked = signalsBlocked(); if (!wasBlocked) blockSignals(true); for (IconItem* item = firstItem(); item; item = item->nextItem()) { if (!item->isSelected()) { item->setSelected(true, false); } } if (!wasBlocked) blockSignals(false); emit signalSelectionChanged(); } void IconView::invertSelection() { bool wasBlocked = signalsBlocked(); if (!wasBlocked) blockSignals(true); for (IconItem* item = firstItem(); item; item = item->nextItem()) { if (!item->isSelected()) { item->setSelected(true, false); } else { item->setSelected(false, false); } } if (!wasBlocked) blockSignals(false); emit signalSelectionChanged(); } void IconView::selectItem(IconItem* item, bool select) { if (!item) return; if (select) { d->selectedItems.replace(item, item); } else { d->selectedItems.remove(item); } emit signalSelectionChanged(); } void IconView::setStoredVisibleItem(IconItem *item) { d->storedVisibleItem = item; } void IconView::insertGroup(IconGroupItem* group) { if (!group) return; if (!d->firstGroup) { d->firstGroup = group; d->lastGroup = group; group->m_prev = 0; group->m_next = 0; } else { d->lastGroup->m_next = group; group->m_prev = d->lastGroup; group->m_next = 0; d->lastGroup = group; } d->storedVisibleItem = findFirstVisibleItem(); startRearrangeTimer(); } void IconView::takeGroup(IconGroupItem* group) { if (!group) return; // this is only to find an alternative visible item if all visible items // are removed IconGroupItem *alternativeVisibleGroup = 0; d->storedVisibleItem = 0; if (group == d->firstGroup) { d->firstGroup = d->firstGroup->m_next; if (d->firstGroup) d->firstGroup->m_prev = 0; else d->firstGroup = d->lastGroup = 0; alternativeVisibleGroup = d->firstGroup; } else if (group == d->lastGroup) { d->lastGroup = d->lastGroup->m_prev; if ( d->lastGroup ) d->lastGroup->m_next = 0; else d->firstGroup = d->lastGroup = 0; alternativeVisibleGroup = d->lastGroup->m_prev; } else { IconGroupItem *i = group; if (i) { if (i->m_prev ) i->m_prev->m_next = i->m_next; if ( i->m_next ) i->m_next->m_prev = i->m_prev; if (i->m_prev) alternativeVisibleGroup = i->m_prev; else alternativeVisibleGroup = i->m_next; } } if (!d->clearing) { d->storedVisibleItem = findFirstVisibleItem(); if (!d->storedVisibleItem && alternativeVisibleGroup) { // find an alternative visible item d->storedVisibleItem = alternativeVisibleGroup->lastItem(); } startRearrangeTimer(); } } void IconView::insertItem(IconItem* item) { if (!item) return; d->storedVisibleItem = findFirstVisibleItem(); startRearrangeTimer(); } void IconView::takeItem(IconItem* item) { if (!item) return; // First remove item from any containers holding it IconViewPriv::ItemContainer *tmp = d->firstContainer; while (tmp) { tmp->items.remove(item); tmp = tmp->next; } // Remove from selected item list d->selectedItems.remove(item); // See bug 161084 if (d->selectedItems.count() || item->isSelected()) d->needEmitSelectionChanged = true; if (d->toolTipItem == item) { d->toolTipItem = 0; d->toolTipTimer->stop(); slotToolTip(); } // if it is current item, change the current item if (d->currItem == item) { d->currItem = item->nextItem(); if (!d->currItem) d->currItem = item->prevItem(); // defer calling d->currItem->setSelected (and emitting the signals) to slotRearrange } d->anchorItem = d->currItem; if (!d->clearing) { d->storedVisibleItem = findFirstVisibleItem(); if (d->storedVisibleItem == item) d->storedVisibleItem = d->currItem; startRearrangeTimer(); } } void IconView::triggerRearrangement() { d->storedVisibleItem = findFirstVisibleItem(); startRearrangeTimer(); } void IconView::setDelayedRearrangement(bool delayed) { // if it is known that e.g. several items will be added or deleted in the next time, // but not from the same event queue thread stack location, it may be desirable to delay // the rearrangeTimer a bit if (delayed) d->rearrangeTimerInterval = 50; else d->rearrangeTimerInterval = 0; } void IconView::startRearrangeTimer() { // We want to reduce the number of updates, but not remove all updates if (!d->rearrangeTimer->isActive()) d->rearrangeTimer->start(d->rearrangeTimerInterval, true); } void IconView::sort() { // first sort the groups for (IconGroupItem* group = d->firstGroup; group; group = group->nextGroup()) { group->sort(); } int gcount = groupCount(); // then sort the groups themselves IconViewPriv::SortableItem *groups = new IconViewPriv::SortableItem[ gcount ]; IconGroupItem *group = d->firstGroup; int i = 0; for ( ; group; group = group->m_next ) groups[ i++ ].group = group; qsort( groups, gcount, sizeof( IconViewPriv::SortableItem ), cmpItems ); IconGroupItem *prev = 0; group = 0; for ( i = 0; i < (int)gcount; ++i ) { group = groups[ i ].group; if ( group ) { group->m_prev = prev; if ( group->m_prev ) group->m_prev->m_next = group; group->m_next = 0; } if ( i == 0 ) d->firstGroup = group; if ( i == (int)gcount - 1 ) d->lastGroup = group; prev = group; } delete [] groups; } void IconView::slotRearrange() { sort(); arrangeItems(); // ensure there is a current item if (!d->currItem) { // set the currItem to first item if (d->firstGroup) d->currItem = d->firstGroup->firstItem(); } d->anchorItem = d->currItem; // ensure there is a selection if (d->selectedItems.isEmpty() && d->currItem) { d->currItem->setSelected(true, true); } else if (d->needEmitSelectionChanged) { emit signalSelectionChanged(); } d->needEmitSelectionChanged = false; // set first visible item if they where stored before update was triggered if (d->storedVisibleItem) { ensureItemVisible(d->storedVisibleItem); // reset to 0 d->storedVisibleItem = 0; } else { ensureItemVisible(d->currItem); } viewport()->update(); } bool IconView::arrangeItems() { int y = 0; int itemW = itemRect().width(); int itemH = itemRect().height(); int maxW = 0; int numItemsPerRow = visibleWidth()/(itemW + d->spacing); bool changed = false; IconGroupItem* group = d->firstGroup; IconItem* item = 0; while (group) { changed = group->move(y) || changed; y += group->rect().height() + d->spacing; item = group->firstItem(); int col = 0; int x = d->spacing; while (item) { changed = item->move(x, y) || changed; x += itemW + d->spacing; col++; if (col >= numItemsPerRow) { x = d->spacing; y += itemH + d->spacing; col = 0; } maxW = TQMAX(maxW, x + itemW); item = item->m_next; } if (col != 0) { y += itemH + d->spacing; } y += d->spacing; group = group->m_next; } viewport()->setUpdatesEnabled(false); resizeContents( maxW, y ); viewport()->setUpdatesEnabled(true); rebuildContainers(); return changed; } TQRect IconView::itemRect() const { return TQRect(0, 0, 100, 100); } TQRect IconView::bannerRect() const { return TQRect(0, 0, visibleWidth(), 0); } void IconView::viewportPaintEvent(TQPaintEvent* pe) { TQRect r(pe->rect()); TQRegion paintRegion(pe->region()); TQPainter painter(viewport()); painter.setClipRegion(paintRegion); // paint any group banners which intersect this paintevent rect for (IconGroupItem* group = d->firstGroup; group; group = group->nextGroup()) { TQRect br(contentsRectToViewport(group->rect())); if (r.intersects(br)) { group->paintBanner(); paintRegion -= TQRegion(br); } } // now paint any items which intersect for (IconViewPriv::ItemContainer* c = d->firstContainer; c; c = c->next) { TQRect cr(contentsRectToViewport(c->rect)); if (r.intersects(cr)) { for (TQValueList::iterator it = c->items.begin(); it != c->items.end(); ++it) { IconItem* item = *it; TQRect ir(contentsRectToViewport(item->rect())); if (r.intersects(ir)) { item->paintItem(); paintRegion -= TQRegion(ir); } } } } painter.setClipRegion(paintRegion); painter.fillRect(r, colorGroup().base()); painter.end(); } TQRect IconView::contentsRectToViewport(const TQRect& r) const { TQRect vr = TQRect(contentsToViewport(TQPoint(r.x(), r.y())), r.size()); return vr; } void IconView::resizeEvent(TQResizeEvent* e) { TQScrollView::resizeEvent(e); triggerRearrangement(); } void IconView::rebuildContainers() { deleteContainers(); IconItem *item = 0; appendContainer(); if (d->firstGroup) item = d->firstGroup->firstItem(); IconViewPriv::ItemContainer* c = d->lastContainer; while (item) { if (c->rect.contains(item->rect())) { c->items.append(item); item = item->nextItem(); } else if (c->rect.intersects(item->rect())) { c->items.append( item ); c = c->next; if (!c) { appendContainer(); c = d->lastContainer; } c->items.append(item); item = item->nextItem(); c = c->prev; } else { if (item->y() < c->rect.y() && c->prev) { c = c->prev; continue; } c = c->next; if (!c) { appendContainer(); c = d->lastContainer; } } } } void IconView::appendContainer() { TQSize s( INT_MAX - 1, RECT_EXTENSION ); if (!d->firstContainer) { d->firstContainer = new IconViewPriv::ItemContainer(0, 0, TQRect(TQPoint(0, 0), s)); d->lastContainer = d->firstContainer; } else { d->lastContainer = new IconViewPriv::ItemContainer( d->lastContainer, 0, TQRect(d->lastContainer->rect.bottomLeft(), s)); } } void IconView::deleteContainers() { IconViewPriv::ItemContainer *c = d->firstContainer; IconViewPriv::ItemContainer *tmp; while (c) { tmp = c->next; delete c; c = tmp; } d->firstContainer = d->lastContainer = 0; } void IconView::leaveEvent(TQEvent *e) { // hide tooltip d->toolTipItem = 0; d->toolTipTimer->stop(); slotToolTip(); // if the mouse leaves the widget we are not dragging // anymore d->dragging = false; TQScrollView::leaveEvent(e); } void IconView::focusOutEvent(TQFocusEvent* e) { // hide tooltip d->toolTipItem = 0; d->toolTipTimer->stop(); slotToolTip(); TQScrollView::focusOutEvent(e); } bool IconView::acceptToolTip(IconItem*, const TQPoint&) { return true; } void IconView::contentsMousePressEvent(TQMouseEvent* e) { d->pressedMoved = false; // hide tooltip d->toolTipItem = 0; d->toolTipTimer->stop(); slotToolTip(); // Delete any existing rubber ------------------------------- if ( d->rubber ) { TQPainter p; p.begin(viewport()); p.setRasterOp(NotROP); p.setPen(TQPen(color0, 1)); p.setBrush(NoBrush); drawRubber(&p); p.end(); delete d->rubber; d->rubber = 0; } if (e->button() == TQt::RightButton) { IconItem* item = findItem(e->pos()); if (item) { IconItem* prevCurrItem = d->currItem; d->currItem = item; d->anchorItem = item; if (prevCurrItem) prevCurrItem->repaint(); if (!item->isSelected()) item->setSelected(true, true); item->repaint(); emit signalRightButtonClicked(item, e->globalPos()); } else { clearSelection(); emit signalRightButtonClicked(e->globalPos()); } return; } IconItem *item = findItem(e->pos()); if (item) { if (e->state() & TQt::ControlButton) { item->setSelected(!item->isSelected(), false); } else if (e->state() & TQt::ShiftButton) { blockSignals(true); if (d->currItem) { clearSelection(); // select all items from/upto the current item bool bwdSelect = false; // find if the current item is before the clicked item for (IconItem* it = item->prevItem(); it; it = it->prevItem()) { if (it == d->currItem) { bwdSelect = true; break; } } if (bwdSelect) { for (IconItem* it = item; it; it = it->prevItem()) { it->setSelected(true, false); if (it == d->currItem) break; } } else { for (IconItem* it = item; it; it = it->nextItem()) { it->setSelected(true, false); if (it == d->currItem) break; } } } else { item->setSelected(true, false); } blockSignals(false); emit signalSelectionChanged(); } else { if (!item->isSelected()) item->setSelected(true, true); } IconItem* prevCurrItem = d->currItem; d->currItem = item; d->anchorItem = item; if (prevCurrItem) prevCurrItem->repaint(); d->currItem->repaint(); d->dragging = true; d->dragStartPos = e->pos(); return; } // Press outside any item. if (!(e->state() & TQt::ControlButton)) { // unselect all if the ctrl button is not pressed clearSelection(); } else { // ctrl is pressed. make sure our current selection is not lost d->prevSelectedItems.clear(); TQPtrDictIterator it( d->selectedItems ); for ( ; it.current(); ++it ) { d->prevSelectedItems.insert(it.current(), it.current()); } } d->rubber = new TQRect( e->x(), e->y(), 0, 0 ); TQPainter p; p.begin( viewport() ); p.setRasterOp( NotROP ); p.setPen( TQPen( color0, 1 ) ); p.setBrush( NoBrush ); drawRubber( &p ); p.end(); } void IconView::drawRubber(TQPainter* p) { if ( !p || !d->rubber ) return; TQRect r(d->rubber->normalize()); r = contentsRectToViewport(r); TQPoint pnt(r.x(), r.y()); style().drawPrimitive(TQStyle::PE_FocusRect, p, TQRect( pnt.x(), pnt.y(), r.width(), r.height() ), colorGroup(), TQStyle::Style_Default, TQStyleOption(colorGroup().base())); } void IconView::contentsMouseMoveEvent(TQMouseEvent* e) { if (e->state() == NoButton) { IconItem* item = findItem(e->pos()); if(d->showTips) { if (!isActiveWindow()) { d->toolTipItem = 0; d->toolTipTimer->stop(); slotToolTip(); return; } if (item != d->toolTipItem) { d->toolTipItem = 0; d->toolTipTimer->stop(); slotToolTip(); if(acceptToolTip(item, e->pos())) { d->toolTipItem = item; d->toolTipTimer->start(500, true); } } if(item == d->toolTipItem && !acceptToolTip(item, e->pos())) { d->toolTipItem = 0; d->toolTipTimer->stop(); slotToolTip(); } } if (TDEGlobalSettings::changeCursorOverIcon()) { if (item && item->clickToOpenRect().contains(e->pos())) setCursor(KCursor::handCursor()); else unsetCursor(); } return; } d->toolTipItem = 0; d->toolTipTimer->stop(); slotToolTip(); if (d->dragging && (e->state() & TQt::LeftButton)) { if ( (d->dragStartPos - e->pos()).manhattanLength() > TQApplication::startDragDistance() ) { startDrag(); } return; } if (!d->rubber) return; TQRect oldRubber = TQRect(*d->rubber); d->rubber->setRight( e->pos().x() ); d->rubber->setBottom( e->pos().y() ); TQRect nr = d->rubber->normalize(); TQRect rubberUnion = nr.unite(oldRubber.normalize()); bool changed = false; TQRegion paintRegion; viewport()->setUpdatesEnabled(false); blockSignals(true); IconViewPriv::ItemContainer *c = d->firstContainer; for (; c; c = c->next) { if ( rubberUnion.intersects(c->rect) ) { for (TQValueList::iterator it = c->items.begin(); it != c->items.end(); ++it) { IconItem* item = *it; if (nr.intersects(item->rect())) { if (!item->isSelected()) { item->setSelected(true, false); changed = true; paintRegion += TQRect(item->rect()); } } else { if (item->isSelected() && !d->prevSelectedItems.find(item)) { item->setSelected(false, false); changed = true; paintRegion += TQRect(item->rect()); } } } } } blockSignals(false); viewport()->setUpdatesEnabled(true); TQRect r = *d->rubber; *d->rubber = oldRubber; TQPainter p; p.begin( viewport() ); p.setRasterOp( NotROP ); p.setPen( TQPen( color0, 1 ) ); p.setBrush( NoBrush ); drawRubber( &p ); p.end(); if (changed) { paintRegion.translate(-contentsX(), -contentsY()); viewport()->repaint(paintRegion); } ensureVisible(e->pos().x(), e->pos().y()); *d->rubber = r; p.begin(viewport()); p.setRasterOp(NotROP); p.setPen(TQPen(color0, 1)); p.setBrush(NoBrush); drawRubber(&p); p.end(); d->pressedMoved = true; if (changed) emit signalSelectionChanged(); } void IconView::contentsMouseReleaseEvent(TQMouseEvent* e) { d->dragging = false; d->prevSelectedItems.clear(); if (d->rubber) { TQPainter p; p.begin( viewport() ); p.setRasterOp( NotROP ); p.setPen( TQPen( color0, 1 ) ); p.setBrush( NoBrush ); drawRubber( &p ); p.end(); delete d->rubber; d->rubber = 0; } if (e->state() == TQt::LeftButton) { if (d->pressedMoved) { emit signalSelectionChanged(); d->pressedMoved = false; return; } // click on item IconItem *item = findItem(e->pos()); if (item) { IconItem* prevCurrItem = d->currItem; item->setSelected(true, true); d->currItem = item; d->anchorItem = item; if (prevCurrItem) prevCurrItem->repaint(); if (TDEGlobalSettings::singleClick()) { if (item->clickToOpenRect().contains(e->pos())) { itemClickedToOpen(item); } } } } } void IconView::contentsWheelEvent(TQWheelEvent* e) { d->toolTipItem = 0; d->toolTipTimer->stop(); slotToolTip(); viewport()->update(); TQScrollView::contentsWheelEvent(e); } void IconView::contentsMouseDoubleClickEvent(TQMouseEvent *e) { if (TDEGlobalSettings::singleClick()) return; IconItem *item = findItem(e->pos()); if (item) { itemClickedToOpen(item); } } void IconView::keyPressEvent(TQKeyEvent* e) { bool handled = false; if (!firstItem()) return; switch ( e->key() ) { case Key_Home: { IconItem* tmp = d->currItem; d->currItem = firstItem(); d->anchorItem = d->currItem; if (tmp) tmp->repaint(); firstItem()->setSelected(true, true); ensureItemVisible(firstItem()); handled = true; break; } case Key_End: { IconItem* tmp = d->currItem; d->currItem = lastItem(); d->anchorItem = d->currItem; if (tmp) tmp->repaint(); lastItem()->setSelected(true, true); ensureItemVisible(lastItem()); handled = true; break; } case Key_Enter: case Key_Return: { if (d->currItem) { emit signalReturnPressed(d->currItem); handled = true; } break; } case Key_Right: { IconItem *item = 0; if (d->currItem) { if (d->currItem->nextItem()) { if (e->state() & TQt::ControlButton) { IconItem* tmp = d->currItem; d->currItem = d->currItem->nextItem(); d->anchorItem = d->currItem; tmp->repaint(); d->currItem->repaint(); item = d->currItem; } else if (e->state() & TQt::ShiftButton) { IconItem* tmp = d->currItem; d->currItem = d->currItem->nextItem(); tmp->repaint(); // if the anchor is behind us, move forward preserving // the previously selected item. otherwise unselect the // previously selected item if (!anchorIsBehind()) tmp->setSelected(false, false); d->currItem->setSelected(true, false); item = d->currItem; } else { IconItem* tmp = d->currItem; d->currItem = d->currItem->nextItem(); d->anchorItem = d->currItem; d->currItem->setSelected(true, true); tmp->repaint(); item = d->currItem; } } } else { d->currItem = firstItem(); d->anchorItem = d->currItem; d->currItem->setSelected(true, true); item = d->currItem; } ensureItemVisible(item); handled = true; break; } case Key_Left: { IconItem *item = 0; if (d->currItem) { if (d->currItem->prevItem()) { if (e->state() & TQt::ControlButton) { IconItem* tmp = d->currItem; d->currItem = d->currItem->prevItem(); d->anchorItem = d->currItem; tmp->repaint(); d->currItem->repaint(); item = d->currItem; } else if (e->state() & TQt::ShiftButton) { IconItem* tmp = d->currItem; d->currItem = d->currItem->prevItem(); tmp->repaint(); // if the anchor is ahead of us, move forward preserving // the previously selected item. otherwise unselect the // previously selected item if (anchorIsBehind()) tmp->setSelected(false, false); d->currItem->setSelected(true, false); item = d->currItem; } else { IconItem* tmp = d->currItem; d->currItem = d->currItem->prevItem(); d->anchorItem = d->currItem; d->currItem->setSelected(true, true); tmp->repaint(); item = d->currItem; } } } else { d->currItem = firstItem(); d->anchorItem = d->currItem; d->currItem->setSelected(true, true); item = d->currItem; } ensureItemVisible(item); handled = true; break; } case Key_Up: { IconItem *item = 0; if (d->currItem) { int x = d->currItem->x() + itemRect().width()/2; int y = d->currItem->y() - d->spacing*2; IconItem *it = 0; while (!it && y > 0) { it = findItem(TQPoint(x,y)); y -= d->spacing * 2; } if (it) { if (e->state() & TQt::ControlButton) { IconItem* tmp = d->currItem; d->currItem = it; d->anchorItem = it; tmp->repaint(); d->currItem->repaint(); item = d->currItem; } else if (e->state() & TQt::ShiftButton) { IconItem* tmp = d->currItem; d->currItem = it; tmp->repaint(); clearSelection(); if (anchorIsBehind()) { for (IconItem* i = d->currItem; i; i = i->prevItem()) { i->setSelected(true, false); if (i == d->anchorItem) break; } } else { for (IconItem* i = d->currItem; i; i = i->nextItem()) { i->setSelected(true, false); if (i == d->anchorItem) break; } } item = d->currItem; } else { IconItem* tmp = d->currItem; d->currItem = it; d->anchorItem = it; d->currItem->setSelected(true, true); tmp->repaint(); item = d->currItem; } } } else { d->currItem = firstItem(); d->anchorItem = d->currItem; d->currItem->setSelected(true, true); item = d->currItem; } ensureItemVisible(item); handled = true; break; } case Key_Down: { IconItem *item = 0; if (d->currItem) { int x = d->currItem->x() + itemRect().width()/2; int y = d->currItem->y() + itemRect().height() + d->spacing*2; IconItem *it = 0; while (!it && y < contentsHeight()) { it = findItem(TQPoint(x,y)); y += d->spacing * 2; } if (it) { if (e->state() & TQt::ControlButton) { IconItem* tmp = d->currItem; d->currItem = it; d->anchorItem = it; tmp->repaint(); d->currItem->repaint(); item = d->currItem; } else if (e->state() & TQt::ShiftButton) { IconItem* tmp = d->currItem; d->currItem = it; tmp->repaint(); clearSelection(); if (anchorIsBehind()) { for (IconItem* i = d->currItem; i; i = i->prevItem()) { i->setSelected(true, false); if (i == d->anchorItem) break; } } else { for (IconItem* i = d->currItem; i; i = i->nextItem()) { i->setSelected(true, false); if (i == d->anchorItem) break; } } item = d->currItem; } else { IconItem* tmp = d->currItem; d->currItem = it; d->anchorItem = it; d->currItem->setSelected(true, true); tmp->repaint(); item = d->currItem; } } } else { d->currItem = firstItem(); d->anchorItem = d->currItem; d->currItem->setSelected(true, true); item = d->currItem; } ensureItemVisible(item); handled = true; break; } case Key_Next: { IconItem *item = 0; if (d->currItem) { TQRect r( 0, d->currItem->y() + visibleHeight(), contentsWidth(), visibleHeight() ); IconItem *ni = findFirstVisibleItem(r, false); if (!ni) { r = TQRect( 0, d->currItem->y() + itemRect().height(), contentsWidth(), contentsHeight() ); ni = findLastVisibleItem(r, false); } if (ni) { IconItem* tmp = d->currItem; d->currItem = ni; d->anchorItem = ni; item = ni; tmp->repaint(); d->currItem->setSelected(true, true); } } else { d->currItem = firstItem(); d->anchorItem = d->currItem; d->currItem->setSelected(true, true); item = d->currItem; } ensureItemVisible(item); handled = true; break; } case Key_Prior: { IconItem *item = 0; if (d->currItem) { TQRect r(0, d->currItem->y() - visibleHeight(), contentsWidth(), visibleHeight() ); IconItem *ni = findFirstVisibleItem(r, false); if (!ni) { r = TQRect( 0, 0, contentsWidth(), d->currItem->y() ); ni = findFirstVisibleItem(r, false); } if (ni) { IconItem* tmp = d->currItem; d->currItem = ni; d->anchorItem = ni; item = ni; tmp->repaint(); d->currItem->setSelected(true, true); } } else { d->currItem = firstItem(); d->anchorItem = d->currItem; d->currItem->setSelected(true, true); item = d->currItem; } ensureItemVisible(item); handled = true; break; } // Key_Space is used as a global shortcut in DigikamApp. // Ctrl+Space comes through, Shift+Space is filtered out. case Key_Space: { if (d->currItem) { if ( (e->state() & TQt::ControlButton) || (e->state() & TQt::ShiftButton) ) { d->currItem->setSelected(!d->currItem->isSelected(), false); } else { if (!d->currItem->isSelected()) d->currItem->setSelected(true, true); } handled = true; } break; } case Key_Menu: { if (d->currItem) { if (!d->currItem->isSelected()) d->currItem->setSelected(true, false); ensureItemVisible(d->currItem); TQRect r(itemRect()); int w = r.width(); int h = r.height(); TQPoint p(d->currItem->x() + w / 2, d->currItem->y() + h / 2); emit signalRightButtonClicked(d->currItem, mapToGlobal(contentsToViewport(p))); } break; } default: break; } if (!handled) { e->ignore(); } else { emit signalSelectionChanged(); viewport()->update(); d->toolTipItem = 0; d->toolTipTimer->stop(); slotToolTip(); } } bool IconView::anchorIsBehind() const { if (!d->anchorItem || !d->currItem) return false; for (IconItem* it = d->anchorItem; it; it = it->nextItem()) { if (it == d->currItem) return true; } return false; } void IconView::startDrag() { } void IconView::ensureItemVisible(IconItem *item) { if ( !item ) return; if ( item->y() == firstItem()->y() ) { TQRect r(itemRect()); int w = r.width(); ensureVisible( item->x() + w / 2, 0, w/2+1, 0 ); } else { TQRect r(itemRect()); int w = r.width(); int h = r.height(); ensureVisible( item->x() + w / 2, item->y() + h / 2, w / 2 + 1, h / 2 + 1 ); } } IconItem* IconView::findFirstVisibleItem(bool useThumbnailRect) const { TQRect r(contentsX(), contentsY(), visibleWidth(), visibleHeight()); return findFirstVisibleItem(r, useThumbnailRect); } IconItem* IconView::findLastVisibleItem(bool useThumbnailRect) const { TQRect r(contentsX(), contentsY(), visibleWidth(), visibleHeight()); return findLastVisibleItem(r, useThumbnailRect); } IconItem* IconView::findFirstVisibleItem(const TQRect& r, bool useThumbnailRect) const { IconViewPriv::ItemContainer *c = d->firstContainer; bool alreadyIntersected = false; IconItem* i = 0; for ( ; c; c = c->next ) { if ( c->rect.intersects( r ) ) { alreadyIntersected = true; for (TQValueList::iterator it = c->items.begin(); it != c->items.end(); ++it) { IconItem *item = *it; // if useThumbnailRect, we only check for the clickToOpenRect, which is the thumbnail, // otherwise, we take the whole item rect if ( r.intersects( useThumbnailRect ? item->clickToOpenRect() : item->rect() ) ) { if ( !i ) { i = item; } else { TQRect r2 = item->rect(); TQRect r3 = i->rect(); if ( r2.y() < r3.y() ) i = item; else if ( r2.y() == r3.y() && r2.x() < r3.x() ) i = item; } } } } else { if ( alreadyIntersected ) break; } } return i; } IconItem* IconView::findLastVisibleItem(const TQRect& r, bool useThumbnailRect) const { IconViewPriv::ItemContainer *c = d->firstContainer; IconItem *i = 0; bool alreadyIntersected = false; for ( ; c; c = c->next ) { if ( c->rect.intersects( r ) ) { alreadyIntersected = true; for (TQValueList::iterator it = c->items.begin(); it != c->items.end(); ++it) { IconItem *item = *it; if ( r.intersects( useThumbnailRect ? item->clickToOpenRect() : item->rect() ) ) { if ( !i ) { i = item; } else { TQRect r2 = item->rect(); TQRect r3 = i->rect(); if ( r2.y() > r3.y() ) i = item; else if ( r2.y() == r3.y() && r2.x() > r3.x() ) i = item; } } } } else { if ( alreadyIntersected ) break; } } return i; } void IconView::drawFrameRaised(TQPainter* p) { TQRect r = frameRect(); int lwidth = lineWidth(); const TQColorGroup & g = colorGroup(); qDrawShadeRect( p, r, g, false, lwidth, midLineWidth() ); } void IconView::drawFrameSunken(TQPainter* p) { TQRect r = frameRect(); int lwidth = lineWidth(); const TQColorGroup & g = colorGroup(); qDrawShadeRect( p, r, g, true, lwidth, midLineWidth() ); } void IconView::setEnableToolTips(bool val) { d->showTips = val; if (!val) { d->toolTipItem = 0; d->toolTipTimer->stop(); slotToolTip(); } } void IconView::slotToolTip() { emit signalShowToolTip(d->toolTipItem); } void IconView::itemClickedToOpen(IconItem* item) { if (!item) return; IconItem* prevCurrItem = d->currItem; d->currItem = item; d->anchorItem = item; if (prevCurrItem) prevCurrItem->repaint(); item->setSelected(true); emit signalDoubleClicked(item); } int IconView::cmpItems(const void *n1, const void *n2) { if ( !n1 || !n2 ) return 0; IconViewPriv::SortableItem *i1 = (IconViewPriv::SortableItem *)n1; IconViewPriv::SortableItem *i2 = (IconViewPriv::SortableItem *)n2; return i1->group->compare( i2->group ); } } // namespace Digikam