]> cloud.milkyroute.net Git - dolphin.git/blob - src/dolphincolumnview.cpp
Port dolphin to the KFileItemDelegate API changes.
[dolphin.git] / src / dolphincolumnview.cpp
1 /***************************************************************************
2 * Copyright (C) 2007 by Peter Penz <peter.penz@gmx.at> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
18 ***************************************************************************/
19
20 #include "dolphincolumnview.h"
21
22 #include "dolphinmodel.h"
23 #include "dolphincontroller.h"
24 #include "dolphinsettings.h"
25
26 #include "dolphin_columnmodesettings.h"
27
28 #include <kcolorutils.h>
29 #include <kcolorscheme.h>
30 #include <kdirlister.h>
31
32 #include <QAbstractProxyModel>
33 #include <QApplication>
34 #include <QPoint>
35 #include <QScrollBar>
36 #include <QTimer>
37 #include <QTimeLine>
38
39 /**
40 * Represents one column inside the DolphinColumnView and has been
41 * extended to respect view options and hovering information.
42 */
43 class ColumnWidget : public QListView
44 {
45 public:
46 ColumnWidget(QWidget* parent,
47 DolphinColumnView* columnView,
48 const KUrl& url);
49 virtual ~ColumnWidget();
50
51 /** Sets the size of the icons. */
52 void setDecorationSize(const QSize& size);
53
54 /**
55 * An active column is defined as column, which shows the same URL
56 * as indicated by the URL navigator. The active column is usually
57 * drawn in a lighter color. All operations are applied to this column.
58 */
59 void setActive(bool active);
60 bool isActive() const;
61
62 /**
63 * Sets the directory URL of the child column that is shown next to
64 * this column. This property is only used for a visual indication
65 * of the shown directory, it does not trigger a loading of the model.
66 */
67 void setChildUrl(const KUrl& url);
68 const KUrl& childUrl() const;
69
70 /** Sets the directory URL that is shown inside the column widget. */
71 void setUrl(const KUrl& url);
72
73 /** Returns the directory URL that is shown inside the column widget. */
74 const KUrl& url() const;
75
76 protected:
77 virtual QStyleOptionViewItem viewOptions() const;
78 virtual void dragEnterEvent(QDragEnterEvent* event);
79 virtual void dragLeaveEvent(QDragLeaveEvent* event);
80 virtual void dragMoveEvent(QDragMoveEvent* event);
81 virtual void dropEvent(QDropEvent* event);
82 virtual void paintEvent(QPaintEvent* event);
83 virtual void mousePressEvent(QMouseEvent* event);
84 virtual void keyPressEvent(QKeyEvent* event);
85 virtual void contextMenuEvent(QContextMenuEvent* event);
86 virtual void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
87
88 private:
89 /** Used by ColumnWidget::setActive(). */
90 void activate();
91
92 /** Used by ColumnWidget::setActive(). */
93 void deactivate();
94
95 private:
96 bool m_active;
97 DolphinColumnView* m_view;
98 KUrl m_url; // URL of the directory that is shown
99 KUrl m_childUrl; // URL of the next column that is shown
100 QStyleOptionViewItem m_viewOptions;
101
102 bool m_dragging; // TODO: remove this property when the issue #160611 is solved in Qt 4.4
103 QRect m_dropRect; // TODO: remove this property when the issue #160611 is solved in Qt 4.4
104 };
105
106 ColumnWidget::ColumnWidget(QWidget* parent,
107 DolphinColumnView* columnView,
108 const KUrl& url) :
109 QListView(parent),
110 m_active(true),
111 m_view(columnView),
112 m_url(url),
113 m_childUrl(),
114 m_dragging(false),
115 m_dropRect()
116 {
117 setMouseTracking(true);
118 viewport()->setAttribute(Qt::WA_Hover);
119 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
120 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
121 setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
122 setSelectionBehavior(SelectItems);
123 setSelectionMode(QAbstractItemView::ExtendedSelection);
124 setDragDropMode(QAbstractItemView::DragDrop);
125 setDropIndicatorShown(false);
126 setFocusPolicy(Qt::NoFocus);
127
128 // apply the column mode settings to the widget
129 const ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
130 Q_ASSERT(settings != 0);
131
132 m_viewOptions = QListView::viewOptions();
133
134 QFont font(settings->fontFamily(), settings->fontSize());
135 font.setItalic(settings->italicFont());
136 font.setBold(settings->boldFont());
137 m_viewOptions.font = font;
138
139 const int iconSize = settings->iconSize();
140 m_viewOptions.decorationSize = QSize(iconSize, iconSize);
141
142 m_viewOptions.showDecorationSelected = true;
143
144 KFileItemDelegate* delegate = new KFileItemDelegate(this);
145 setItemDelegate(delegate);
146
147 activate();
148
149 connect(this, SIGNAL(entered(const QModelIndex&)),
150 m_view->m_controller, SLOT(emitItemEntered(const QModelIndex&)));
151 connect(this, SIGNAL(viewportEntered()),
152 m_view->m_controller, SLOT(emitViewportEntered()));
153 }
154
155 ColumnWidget::~ColumnWidget()
156 {
157 }
158
159 void ColumnWidget::setDecorationSize(const QSize& size)
160 {
161 m_viewOptions.decorationSize = size;
162 doItemsLayout();
163 }
164
165 void ColumnWidget::setActive(bool active)
166 {
167 if (m_active == active) {
168 return;
169 }
170
171 m_active = active;
172
173 if (active) {
174 activate();
175 } else {
176 deactivate();
177 }
178 }
179
180 inline bool ColumnWidget::isActive() const
181 {
182 return m_active;
183 }
184
185 inline void ColumnWidget::setChildUrl(const KUrl& url)
186 {
187 m_childUrl = url;
188 }
189
190 inline const KUrl& ColumnWidget::childUrl() const
191 {
192 return m_childUrl;
193 }
194
195 inline void ColumnWidget::setUrl(const KUrl& url)
196 {
197 m_url = url;
198 }
199
200 inline const KUrl& ColumnWidget::url() const
201 {
202 return m_url;
203 }
204
205 inline QStyleOptionViewItem ColumnWidget::viewOptions() const
206 {
207 return m_viewOptions;
208 }
209
210 void ColumnWidget::dragEnterEvent(QDragEnterEvent* event)
211 {
212 if (event->mimeData()->hasUrls()) {
213 event->acceptProposedAction();
214 }
215
216 m_dragging = true;
217 }
218
219 void ColumnWidget::dragLeaveEvent(QDragLeaveEvent* event)
220 {
221 QListView::dragLeaveEvent(event);
222
223 // TODO: remove this code when the issue #160611 is solved in Qt 4.4
224 m_dragging = false;
225 setDirtyRegion(m_dropRect);
226 }
227
228 void ColumnWidget::dragMoveEvent(QDragMoveEvent* event)
229 {
230 QListView::dragMoveEvent(event);
231
232 // TODO: remove this code when the issue #160611 is solved in Qt 4.4
233 const QModelIndex index = indexAt(event->pos());
234 setDirtyRegion(m_dropRect);
235 m_dropRect = visualRect(index);
236 setDirtyRegion(m_dropRect);
237 }
238
239 void ColumnWidget::dropEvent(QDropEvent* event)
240 {
241 const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
242 if (!urls.isEmpty()) {
243 event->acceptProposedAction();
244 m_view->m_controller->indicateDroppedUrls(urls,
245 url(),
246 indexAt(event->pos()),
247 event->source());
248 }
249 QListView::dropEvent(event);
250 m_dragging = false;
251 }
252
253 void ColumnWidget::paintEvent(QPaintEvent* event)
254 {
255 if (!m_childUrl.isEmpty()) {
256 // indicate the shown URL of the next column by highlighting the shown folder item
257 const QModelIndex dirIndex = m_view->m_dolphinModel->indexForUrl(m_childUrl);
258 const QModelIndex proxyIndex = m_view->m_proxyModel->mapFromSource(dirIndex);
259 if (proxyIndex.isValid() && !selectionModel()->isSelected(proxyIndex)) {
260 const QRect itemRect = visualRect(proxyIndex);
261 QPainter painter(viewport());
262 painter.save();
263
264 QColor color = KColorScheme(QPalette::Active, KColorScheme::View).foreground().color();
265 color.setAlpha(32);
266 painter.setPen(Qt::NoPen);
267 painter.setBrush(color);
268 painter.drawRect(itemRect);
269
270 painter.restore();
271 }
272 }
273
274 QListView::paintEvent(event);
275
276 // TODO: remove this code when the issue #160611 is solved in Qt 4.4
277 if (m_dragging) {
278 const QBrush& brush = m_viewOptions.palette.brush(QPalette::Normal, QPalette::Highlight);
279 DolphinController::drawHoverIndication(viewport(), m_dropRect, brush);
280 }
281 }
282
283 void ColumnWidget::mousePressEvent(QMouseEvent* event)
284 {
285 if (!m_active) {
286 m_view->requestActivation(this);
287 }
288
289 QListView::mousePressEvent(event);
290 }
291
292 void ColumnWidget::keyPressEvent(QKeyEvent* event)
293 {
294 QListView::keyPressEvent(event);
295
296 const QItemSelectionModel* selModel = selectionModel();
297 const QModelIndex currentIndex = selModel->currentIndex();
298 const bool triggerItem = currentIndex.isValid()
299 && (event->key() == Qt::Key_Return)
300 && (selModel->selectedIndexes().count() <= 1);
301 if (triggerItem) {
302 m_view->m_controller->triggerItem(currentIndex);
303 }
304 }
305
306 void ColumnWidget::contextMenuEvent(QContextMenuEvent* event)
307 {
308 if (!m_active) {
309 m_view->requestActivation(this);
310 }
311
312 QListView::contextMenuEvent(event);
313
314 const QModelIndex index = indexAt(event->pos());
315 if (index.isValid() || m_active) {
316 // Only open a context menu above an item or if the mouse is above
317 // the active column.
318 const QPoint pos = m_view->viewport()->mapFromGlobal(event->globalPos());
319 m_view->m_controller->triggerContextMenuRequest(pos);
320 }
321 }
322
323 void ColumnWidget::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
324 {
325 QListView::selectionChanged(selected, deselected);
326
327 QItemSelectionModel* selModel = m_view->selectionModel();
328 selModel->select(selected, QItemSelectionModel::Select);
329 selModel->select(deselected, QItemSelectionModel::Deselect);
330 }
331
332 void ColumnWidget::activate()
333 {
334 if (m_view->hasFocus()) {
335 setFocus(Qt::OtherFocusReason);
336 }
337 m_view->setFocusProxy(this);
338
339 // TODO: Connecting to the signal 'activated()' is not possible, as kstyle
340 // does not forward the single vs. doubleclick to it yet (KDE 4.1?). Hence it is
341 // necessary connecting the signal 'singleClick()' or 'doubleClick'.
342 if (KGlobalSettings::singleClick()) {
343 connect(this, SIGNAL(clicked(const QModelIndex&)),
344 m_view->m_controller, SLOT(triggerItem(const QModelIndex&)));
345 } else {
346 connect(this, SIGNAL(doubleClicked(const QModelIndex&)),
347 m_view->m_controller, SLOT(triggerItem(const QModelIndex&)));
348 }
349
350 const QColor bgColor = KColorScheme(QPalette::Active, KColorScheme::View).background().color();
351 QPalette palette = viewport()->palette();
352 palette.setColor(viewport()->backgroundRole(), bgColor);
353 viewport()->setPalette(palette);
354
355 if (!m_childUrl.isEmpty()) {
356 // assure that the current index is set on the index that represents
357 // the child URL
358 const QModelIndex dirIndex = m_view->m_dolphinModel->indexForUrl(m_childUrl);
359 const QModelIndex proxyIndex = m_view->m_proxyModel->mapFromSource(dirIndex);
360 selectionModel()->setCurrentIndex(proxyIndex, QItemSelectionModel::Current);
361 }
362
363 update();
364 }
365
366 void ColumnWidget::deactivate()
367 {
368 // TODO: Connecting to the signal 'activated()' is not possible, as kstyle
369 // does not forward the single vs. doubleclick to it yet (KDE 4.1?). Hence it is
370 // necessary connecting the signal 'singleClick()' or 'doubleClick'.
371 if (KGlobalSettings::singleClick()) {
372 disconnect(this, SIGNAL(clicked(const QModelIndex&)),
373 m_view->m_controller, SLOT(triggerItem(const QModelIndex&)));
374 } else {
375 disconnect(this, SIGNAL(doubleClicked(const QModelIndex&)),
376 m_view->m_controller, SLOT(triggerItem(const QModelIndex&)));
377 }
378
379 const QPalette palette = m_view->viewport()->palette();
380 viewport()->setPalette(palette);
381
382 selectionModel()->clear();
383 update();
384 }
385
386 // ---
387
388 DolphinColumnView::DolphinColumnView(QWidget* parent, DolphinController* controller) :
389 QAbstractItemView(parent),
390 m_controller(controller),
391 m_restoreActiveColumnFocus(false),
392 m_dirListerCompleted(false),
393 m_index(-1),
394 m_contentX(0),
395 m_columns(),
396 m_animation(0),
397 m_dolphinModel(0),
398 m_proxyModel(0)
399 {
400 Q_ASSERT(controller != 0);
401
402 setAcceptDrops(true);
403 setDragDropMode(QAbstractItemView::DragDrop);
404 setDropIndicatorShown(false);
405 setSelectionMode(ExtendedSelection);
406
407 connect(this, SIGNAL(entered(const QModelIndex&)),
408 controller, SLOT(emitItemEntered(const QModelIndex&)));
409 connect(this, SIGNAL(viewportEntered()),
410 controller, SLOT(emitViewportEntered()));
411 connect(controller, SIGNAL(zoomIn()),
412 this, SLOT(zoomIn()));
413 connect(controller, SIGNAL(zoomOut()),
414 this, SLOT(zoomOut()));
415 connect(controller, SIGNAL(urlChanged(const KUrl&)),
416 this, SLOT(showColumn(const KUrl&)));
417
418 connect(horizontalScrollBar(), SIGNAL(valueChanged(int)),
419 this, SLOT(moveContentHorizontally(int)));
420
421 m_animation = new QTimeLine(500, this);
422 connect(m_animation, SIGNAL(frameChanged(int)), horizontalScrollBar(), SLOT(setValue(int)));
423
424 ColumnWidget* column = new ColumnWidget(viewport(), this, m_controller->url());
425 m_columns.append(column);
426 setActiveColumnIndex(0);
427
428 updateDecorationSize();
429
430 // dim the background of the viewport
431 QColor bgColor = KColorScheme(QPalette::Active, KColorScheme::View).background().color();
432 const QColor fgColor = KColorScheme(QPalette::Active, KColorScheme::View).foreground().color();
433 bgColor = KColorUtils::mix(bgColor, fgColor, 0.04);
434
435 QPalette palette = viewport()->palette();
436 palette.setColor(viewport()->backgroundRole(), bgColor);
437 viewport()->setPalette(palette);
438 }
439
440 DolphinColumnView::~DolphinColumnView()
441 {
442 }
443
444 QModelIndex DolphinColumnView::indexAt(const QPoint& point) const
445 {
446 foreach (ColumnWidget* column, m_columns) {
447 const QPoint topLeft = column->frameGeometry().topLeft();
448 const QPoint adjustedPoint(point.x() - topLeft.x(), point.y() - topLeft.y());
449 const QModelIndex index = column->indexAt(adjustedPoint);
450 if (index.isValid()) {
451 return index;
452 }
453 }
454
455 return QModelIndex();
456 }
457
458 void DolphinColumnView::scrollTo(const QModelIndex& index, ScrollHint hint)
459 {
460 activeColumn()->scrollTo(index, hint);
461 }
462
463 QRect DolphinColumnView::visualRect(const QModelIndex& index) const
464 {
465 return activeColumn()->visualRect(index);
466 }
467
468 void DolphinColumnView::setModel(QAbstractItemModel* model)
469 {
470 if (m_dolphinModel != 0) {
471 m_dolphinModel->disconnect(this);
472 }
473
474 m_proxyModel = static_cast<QAbstractProxyModel*>(model);
475 m_dolphinModel = static_cast<DolphinModel*>(m_proxyModel->sourceModel());
476 connect(m_dolphinModel, SIGNAL(expand(const QModelIndex&)),
477 this, SLOT(triggerReloadColumns(const QModelIndex&)));
478
479 KDirLister* dirLister = m_dolphinModel->dirLister();
480 connect(dirLister, SIGNAL(started(const KUrl&)),
481 this, SLOT(slotDirListerStarted(const KUrl&)));
482 connect(dirLister, SIGNAL(completed()),
483 this, SLOT(slotDirListerCompleted()));
484
485 activeColumn()->setModel(model);
486 QAbstractItemView::setModel(model);
487 }
488
489 void DolphinColumnView::invertSelection()
490 {
491 // TODO: this approach of inverting the selection is quite slow. It should
492 // be possible to speedup the implementation by using QItemSelection, but
493 // all adempts have failed yet...
494
495 ColumnWidget* column = activeColumn();
496 QItemSelectionModel* selModel = column->selectionModel();
497
498 KDirLister* dirLister = m_dolphinModel->dirLister();
499 const KFileItemList list = dirLister->itemsForDir(column->url());
500 foreach (const KFileItem item, list) {
501 const QModelIndex index = m_dolphinModel->indexForUrl(item.url());
502 selModel->select(m_proxyModel->mapFromSource(index), QItemSelectionModel::Toggle);
503 }
504 }
505
506 void DolphinColumnView::reload()
507 {
508 // Due to the reloading of the model all columns will be reset to show
509 // the same content as the first column. As this is not wanted, all columns
510 // except of the first column are temporary hidden until the root index can
511 // be updated again.
512 m_restoreActiveColumnFocus = false;
513 QList<ColumnWidget*>::iterator start = m_columns.begin() + 1;
514 QList<ColumnWidget*>::iterator end = m_columns.end();
515 for (QList<ColumnWidget*>::iterator it = start; it != end; ++it) {
516 ColumnWidget* column = (*it);
517 if (column->isActive() && column->hasFocus()) {
518 // because of hiding the column, it will lose the focus
519 // -> remember that the focus should be restored after reloading
520 m_restoreActiveColumnFocus = true;
521 }
522 column->hide();
523 }
524
525 // all columns are hidden, now reload the directory lister
526 KDirLister* dirLister = m_dolphinModel->dirLister();
527 const KUrl& rootUrl = m_columns[0]->url();
528 dirLister->openUrl(rootUrl, false, true);
529 updateColumns();
530 }
531
532 void DolphinColumnView::showColumn(const KUrl& url)
533 {
534 const KUrl& rootUrl = m_columns[0]->url();
535 if (!rootUrl.isParentOf(url)) {
536 // the URL is no child URL of the column view, hence clear all columns
537 // and reset the root column
538 QList<ColumnWidget*>::iterator start = m_columns.begin() + 1;
539 QList<ColumnWidget*>::iterator end = m_columns.end();
540 for (QList<ColumnWidget*>::iterator it = start; it != end; ++it) {
541 (*it)->deleteLater();
542 }
543 m_columns.erase(start, end);
544 m_index = 0;
545 m_columns[0]->setActive(true);
546 m_columns[0]->setUrl(url);
547 assureVisibleActiveColumn();
548 return;
549 }
550
551 KDirLister* dirLister = m_dolphinModel->dirLister();
552 const KUrl dirListerUrl = dirLister->url();
553 if (dirListerUrl != rootUrl) {
554 // It is possible that root URL of the directory lister is adjusted
555 // after creating the column widget (e. g. when restoring the history
556 // having a different root URL than the controller indicates).
557 m_columns[0]->setUrl(dirListerUrl);
558 }
559
560 int columnIndex = 0;
561 foreach (ColumnWidget* column, m_columns) {
562 if (column->url() == url) {
563 // the column represents already the requested URL, hence activate it
564 requestActivation(column);
565 return;
566 } else if (!column->url().isParentOf(url)) {
567 // the column is no parent of the requested URL, hence
568 // just delete all remaining columns
569 if (columnIndex > 0) {
570 QList<ColumnWidget*>::iterator start = m_columns.begin() + columnIndex;
571 QList<ColumnWidget*>::iterator end = m_columns.end();
572 for (QList<ColumnWidget*>::iterator it = start; it != end; ++it) {
573 (*it)->deleteLater();
574 }
575 m_columns.erase(start, end);
576
577 const int maxIndex = m_columns.count() - 1;
578 Q_ASSERT(maxIndex >= 0);
579 if (m_index > maxIndex) {
580 m_index = maxIndex;
581 }
582 break;
583 }
584 }
585 ++columnIndex;
586 }
587
588 // Create missing columns. Assuming that the path is "/home/peter/Temp/" and
589 // the target path is "/home/peter/Temp/a/b/c/", then the columns "a", "b" and
590 // "c" will be created.
591 const int lastIndex = m_columns.count() - 1;
592 Q_ASSERT(lastIndex >= 0);
593
594 const KUrl& activeUrl = m_columns[lastIndex]->url();
595 Q_ASSERT(activeUrl.isParentOf(url));
596 Q_ASSERT(activeUrl != url);
597
598 QString path = activeUrl.url(KUrl::AddTrailingSlash);
599 const QString targetPath = url.url(KUrl::AddTrailingSlash);
600
601 columnIndex = lastIndex;
602 int slashIndex = path.count('/');
603 bool hasSubPath = (slashIndex >= 0);
604 while (hasSubPath) {
605 const QString subPath = targetPath.section('/', slashIndex, slashIndex);
606 if (subPath.isEmpty()) {
607 hasSubPath = false;
608 } else {
609 path += subPath + '/';
610 ++slashIndex;
611
612 const KUrl childUrl = KUrl(path);
613 const QModelIndex dirIndex = m_dolphinModel->indexForUrl(KUrl(path));
614 const QModelIndex proxyIndex = m_proxyModel->mapFromSource(dirIndex);
615
616 m_columns[columnIndex]->setChildUrl(childUrl);
617 columnIndex++;
618
619 ColumnWidget* column = new ColumnWidget(viewport(), this, childUrl);
620 column->setModel(model());
621 column->setRootIndex(proxyIndex);
622 column->setActive(false);
623
624 m_columns.append(column);
625
626 // Before invoking layoutColumns() the column must be set visible temporary.
627 // To prevent a flickering the initial geometry is set to a hidden position.
628 column->setGeometry(QRect(-1, -1, 1, 1));
629 column->show();
630 layoutColumns();
631 updateScrollBar();
632
633 // the layout is finished, now let the column be invisible until it
634 // gets a valid root index due to expandToActiveUrl()
635 column->hide();
636 }
637 }
638
639 // set the last column as active column without modifying the controller
640 // and hence the history
641 activeColumn()->setActive(false);
642 m_index = columnIndex;
643 activeColumn()->setActive(true);
644 assureVisibleActiveColumn();
645 }
646
647 void DolphinColumnView::selectAll()
648 {
649 activeColumn()->selectAll();
650 }
651
652 bool DolphinColumnView::isIndexHidden(const QModelIndex& index) const
653 {
654 Q_UNUSED(index);
655 return false;//activeColumn()->isIndexHidden(index);
656 }
657
658 QModelIndex DolphinColumnView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
659 {
660 // Parts of this code have been taken from QColumnView::moveCursor().
661 // Copyright (C) 1992-2007 Trolltech ASA.
662
663 Q_UNUSED(modifiers);
664 if (model() == 0) {
665 return QModelIndex();
666 }
667
668 const QModelIndex current = currentIndex();
669 if (isRightToLeft()) {
670 if (cursorAction == MoveLeft) {
671 cursorAction = MoveRight;
672 } else if (cursorAction == MoveRight) {
673 cursorAction = MoveLeft;
674 }
675 }
676
677 switch (cursorAction) {
678 case MoveLeft:
679 if (m_index > 0) {
680 setActiveColumnIndex(m_index - 1);
681 }
682 break;
683
684 case MoveRight:
685 if (m_index < m_columns.count() - 1) {
686 setActiveColumnIndex(m_index + 1);
687 }
688 break;
689
690 default:
691 break;
692 }
693
694 return QModelIndex();
695 }
696
697 void DolphinColumnView::setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags flags)
698 {
699 Q_UNUSED(rect);
700 Q_UNUSED(flags);
701 //activeColumn()->setSelection(rect, flags);
702 }
703
704 QRegion DolphinColumnView::visualRegionForSelection(const QItemSelection& selection) const
705 {
706 Q_UNUSED(selection);
707 return QRegion(); //activeColumn()->visualRegionForSelection(selection);
708 }
709
710 int DolphinColumnView::horizontalOffset() const
711 {
712 return -m_contentX;
713 }
714
715 int DolphinColumnView::verticalOffset() const
716 {
717 return 0;
718 }
719
720 void DolphinColumnView::mousePressEvent(QMouseEvent* event)
721 {
722 m_controller->triggerActivation();
723 QAbstractItemView::mousePressEvent(event);
724 }
725
726 void DolphinColumnView::resizeEvent(QResizeEvent* event)
727 {
728 QAbstractItemView::resizeEvent(event);
729 layoutColumns();
730 updateScrollBar();
731 }
732
733 void DolphinColumnView::zoomIn()
734 {
735 if (isZoomInPossible()) {
736 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
737 switch (settings->iconSize()) {
738 case KIconLoader::SizeSmall: settings->setIconSize(KIconLoader::SizeMedium); break;
739 case KIconLoader::SizeMedium: settings->setIconSize(KIconLoader::SizeLarge); break;
740 default: Q_ASSERT(false); break;
741 }
742 updateDecorationSize();
743 }
744 }
745
746 void DolphinColumnView::zoomOut()
747 {
748 if (isZoomOutPossible()) {
749 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
750 switch (settings->iconSize()) {
751 case KIconLoader::SizeLarge: settings->setIconSize(KIconLoader::SizeMedium); break;
752 case KIconLoader::SizeMedium: settings->setIconSize(KIconLoader::SizeSmall); break;
753 default: Q_ASSERT(false); break;
754 }
755 updateDecorationSize();
756 }
757 }
758
759 void DolphinColumnView::moveContentHorizontally(int x)
760 {
761 m_contentX = isRightToLeft() ? +x : -x;
762 layoutColumns();
763 }
764
765 void DolphinColumnView::updateDecorationSize()
766 {
767 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
768 const int iconSize = settings->iconSize();
769
770 foreach (QObject* object, viewport()->children()) {
771 if (object->inherits("QListView")) {
772 ColumnWidget* widget = static_cast<ColumnWidget*>(object);
773 widget->setDecorationSize(QSize(iconSize, iconSize));
774 }
775 }
776
777 m_controller->setZoomInPossible(isZoomInPossible());
778 m_controller->setZoomOutPossible(isZoomOutPossible());
779
780 doItemsLayout();
781 }
782
783 void DolphinColumnView::expandToActiveUrl()
784 {
785 const int lastIndex = m_columns.count() - 1;
786 Q_ASSERT(lastIndex >= 0);
787 const KUrl& activeUrl = m_columns[lastIndex]->url();
788 const KUrl rootUrl = m_dolphinModel->dirLister()->url();
789 const bool expand = m_dirListerCompleted
790 && rootUrl.isParentOf(activeUrl)
791 && !rootUrl.equals(activeUrl, KUrl::CompareWithoutTrailingSlash);
792 if (expand) {
793 m_dolphinModel->expandToUrl(activeUrl);
794 }
795 updateColumns();
796 }
797
798 void DolphinColumnView::triggerUpdateColumns(const QModelIndex& index)
799 {
800 Q_UNUSED(index);
801 // the updating of the columns may not be done in the context of this slot
802 QMetaObject::invokeMethod(this, "updateColumns", Qt::QueuedConnection);
803 }
804
805 void DolphinColumnView::updateColumns()
806 {
807 const int end = m_columns.count() - 2; // next to last column
808 for (int i = 0; i <= end; ++i) {
809 ColumnWidget* nextColumn = m_columns[i + 1];
810 const QModelIndex rootIndex = nextColumn->rootIndex();
811 if (rootIndex.isValid()) {
812 nextColumn->show();
813 } else {
814 const QModelIndex dirIndex = m_dolphinModel->indexForUrl(m_columns[i]->childUrl());
815 const QModelIndex proxyIndex = m_proxyModel->mapFromSource(dirIndex);
816 if (proxyIndex.isValid()) {
817 nextColumn->setRootIndex(proxyIndex);
818 nextColumn->show();
819 if (nextColumn->isActive() && m_restoreActiveColumnFocus) {
820 nextColumn->setFocus();
821 m_restoreActiveColumnFocus = false;
822 }
823 }
824 }
825 }
826 }
827
828 void DolphinColumnView::slotDirListerStarted(const KUrl& url)
829 {
830 Q_UNUSED(url);
831 m_dirListerCompleted = false;
832 }
833
834 void DolphinColumnView::slotDirListerCompleted()
835 {
836 m_dirListerCompleted = true;
837 QMetaObject::invokeMethod(this, "expandToActiveUrl", Qt::QueuedConnection);
838 }
839
840 bool DolphinColumnView::isZoomInPossible() const
841 {
842 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
843 return settings->iconSize() < KIconLoader::SizeLarge;
844 }
845
846 bool DolphinColumnView::isZoomOutPossible() const
847 {
848 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
849 return settings->iconSize() > KIconLoader::SizeSmall;
850 }
851
852 void DolphinColumnView::setActiveColumnIndex(int index)
853 {
854 if (m_index == index) {
855 return;
856 }
857
858 const bool hasActiveColumn = (m_index >= 0);
859 if (hasActiveColumn) {
860 m_columns[m_index]->setActive(false);
861 }
862
863 m_index = index;
864 m_columns[m_index]->setActive(true);
865
866 m_controller->setUrl(m_columns[m_index]->url());
867
868 assureVisibleActiveColumn();
869 }
870
871 void DolphinColumnView::layoutColumns()
872 {
873 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
874 const int columnWidth = settings->columnWidth();
875 if (isRightToLeft()) {
876 int x = viewport()->width() - columnWidth + m_contentX;
877 foreach (ColumnWidget* column, m_columns) {
878 column->setGeometry(QRect(x, 0, columnWidth, viewport()->height()));
879 x -= columnWidth;
880 }
881 } else {
882 int x = m_contentX;
883 foreach (ColumnWidget* column, m_columns) {
884 column->setGeometry(QRect(x, 0, columnWidth, viewport()->height()));
885 x += columnWidth;
886 }
887 }
888 }
889
890 void DolphinColumnView::updateScrollBar()
891 {
892 int contentWidth = 0;
893 foreach (ColumnWidget* column, m_columns) {
894 contentWidth += column->width();
895 }
896
897 horizontalScrollBar()->setPageStep(contentWidth);
898 horizontalScrollBar()->setRange(0, contentWidth - viewport()->width());
899 }
900
901 void DolphinColumnView::assureVisibleActiveColumn()
902 {
903 const int viewportWidth = viewport()->width();
904 const int x = activeColumn()->x();
905 const int width = activeColumn()->width();
906 if (x + width > viewportWidth) {
907 const int newContentX = m_contentX - x - width + viewportWidth;
908 if (isRightToLeft()) {
909 m_animation->setFrameRange(m_contentX, newContentX);
910 } else {
911 m_animation->setFrameRange(-m_contentX, -newContentX);
912 }
913 m_animation->start();
914 } else if (x < 0) {
915 const int newContentX = m_contentX - x;
916 if (isRightToLeft()) {
917 m_animation->setFrameRange(m_contentX, newContentX);
918 } else {
919 m_animation->setFrameRange(-m_contentX, -newContentX);
920 }
921 m_animation->start();
922 }
923 }
924
925 void DolphinColumnView::requestActivation(ColumnWidget* column)
926 {
927 if (column->isActive()) {
928 assureVisibleActiveColumn();
929 } else {
930 int index = 0;
931 foreach (ColumnWidget* currColumn, m_columns) {
932 if (currColumn == column) {
933 setActiveColumnIndex(index);
934 return;
935 }
936 ++index;
937 }
938 }
939 }
940
941 #include "dolphincolumnview.moc"