]> cloud.milkyroute.net Git - dolphin.git/blob - src/dolphincolumnview.cpp
Listen to the directory lister signals to know whether the directory lister is still...
[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 inline 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 // TODO: enable ScrollPerPixel again as soon as a Qt-patch
122 // is supplied which fixes a possible crash
123 //setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
124 setSelectionBehavior(SelectItems);
125 setSelectionMode(QAbstractItemView::ExtendedSelection);
126 setDragDropMode(QAbstractItemView::DragDrop);
127 setDropIndicatorShown(false);
128 setFocusPolicy(Qt::NoFocus);
129
130 // apply the column mode settings to the widget
131 const ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
132 Q_ASSERT(settings != 0);
133
134 m_viewOptions = QListView::viewOptions();
135
136 QFont font(settings->fontFamily(), settings->fontSize());
137 font.setItalic(settings->italicFont());
138 font.setBold(settings->boldFont());
139 m_viewOptions.font = font;
140
141 const int iconSize = settings->iconSize();
142 m_viewOptions.decorationSize = QSize(iconSize, iconSize);
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 const KUrl& ColumnWidget::url() const
201 {
202 return m_url;
203 }
204
205 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 ColumnWidget* column = new ColumnWidget(viewport(), this, m_controller->url());
422 m_columns.append(column);
423 setActiveColumnIndex(0);
424
425 updateDecorationSize();
426
427 m_animation = new QTimeLine(500, this);
428 connect(m_animation, SIGNAL(frameChanged(int)), horizontalScrollBar(), SLOT(setValue(int)));
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 (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 connect(dirLister, SIGNAL(completed()),
528 this, SLOT(expandToActiveUrl()));
529 const KUrl& rootUrl = m_columns[0]->url();
530 dirLister->openUrl(rootUrl, false, true);
531 reloadColumns();
532 }
533
534 void DolphinColumnView::showColumn(const KUrl& url)
535 {
536 const KUrl& rootUrl = m_columns[0]->url();
537 if (!rootUrl.isParentOf(url)) {
538 // the URL is no child URL of the column view, hence clear all columns
539 // and reset the root column
540 QList<ColumnWidget*>::iterator start = m_columns.begin() + 1;
541 QList<ColumnWidget*>::iterator end = m_columns.end();
542 for (QList<ColumnWidget*>::iterator it = start; it != end; ++it) {
543 (*it)->deleteLater();
544 }
545 m_columns.erase(start, end);
546 m_index = 0;
547 m_columns[0]->setActive(true);
548 m_columns[0]->setUrl(url);
549 assureVisibleActiveColumn();
550 return;
551 }
552
553 KDirLister* dirLister = m_dolphinModel->dirLister();
554 const KUrl dirListerUrl = dirLister->url();
555 if (dirListerUrl != rootUrl) {
556 // It is possible that root URL of the directory lister is adjusted
557 // after creating the column widget (e. g. when restoring the history
558 // having a different root URL than the controller indicates).
559 m_columns[0]->setUrl(dirListerUrl);
560 }
561
562 int columnIndex = 0;
563 foreach (ColumnWidget* column, m_columns) {
564 if (column->url() == url) {
565 // the column represents already the requested URL, hence activate it
566 requestActivation(column);
567 return;
568 } else if (!column->url().isParentOf(url)) {
569 // the column is no parent of the requested URL, hence
570 // just delete all remaining columns
571 if (columnIndex > 0) {
572 QList<ColumnWidget*>::iterator start = m_columns.begin() + columnIndex;
573 QList<ColumnWidget*>::iterator end = m_columns.end();
574 for (QList<ColumnWidget*>::iterator it = start; it != end; ++it) {
575 (*it)->deleteLater();
576 }
577 m_columns.erase(start, end);
578
579 const int maxIndex = m_columns.count() - 1;
580 Q_ASSERT(maxIndex >= 0);
581 if (m_index > maxIndex) {
582 m_index = maxIndex;
583 }
584 break;
585 }
586 }
587 ++columnIndex;
588 }
589
590 // Create missing columns. Assuming that the path is "/home/peter/Temp/" and
591 // the target path is "/home/peter/Temp/a/b/c/", then the columns "a", "b" and
592 // "c" will be created.
593 const int lastIndex = m_columns.count() - 1;
594 Q_ASSERT(lastIndex >= 0);
595
596 const KUrl& activeUrl = m_columns[lastIndex]->url();
597 Q_ASSERT(activeUrl.isParentOf(url));
598 Q_ASSERT(activeUrl != url);
599
600 QString path = activeUrl.url(KUrl::AddTrailingSlash);
601 const QString targetPath = url.url(KUrl::AddTrailingSlash);
602
603 columnIndex = lastIndex;
604 int slashIndex = path.count('/');
605 bool hasSubPath = (slashIndex >= 0);
606 while (hasSubPath) {
607 const QString subPath = targetPath.section('/', slashIndex, slashIndex);
608 if (subPath.isEmpty()) {
609 hasSubPath = false;
610 } else {
611 path += subPath + '/';
612 ++slashIndex;
613
614 const KUrl childUrl = KUrl(path);
615 const QModelIndex dirIndex = m_dolphinModel->indexForUrl(KUrl(path));
616 const QModelIndex proxyIndex = m_proxyModel->mapFromSource(dirIndex);
617
618 m_columns[columnIndex]->setChildUrl(childUrl);
619 columnIndex++;
620
621 ColumnWidget* column = new ColumnWidget(viewport(), this, childUrl);
622 column->setVerticalScrollMode(ColumnWidget::ScrollPerPixel);
623 column->setHorizontalScrollMode(ColumnWidget::ScrollPerPixel);
624 column->setModel(model());
625 column->setRootIndex(proxyIndex);
626 column->setActive(false);
627
628 m_columns.append(column);
629
630 // Before invoking layoutColumns() the column must be set visible temporary.
631 // To prevent a flickering the initial geometry is set to a hidden position.
632 column->setGeometry(QRect(-1, -1, 1, 1));
633 column->show();
634 layoutColumns();
635 updateScrollBar();
636
637 // the layout is finished, now let the column be invisible until it
638 // gets a valid root index due to expandToActiveUrl()
639 column->hide();
640 }
641 }
642
643 // set the last column as active column without modifying the controller
644 // and hence the history
645 activeColumn()->setActive(false);
646 m_index = columnIndex;
647 activeColumn()->setActive(true);
648
649 reloadColumns();
650
651 // reloadColumns() is enough for simple use cases where only one column is added.
652 // However when exchanging several columns a more complex synchronization must be
653 // done by invoking synchronize(). The delay is an optimization for default use
654 // cases and gives the directory lister the chance to be already finished when
655 // synchronize() is invoked, which assures zero flickering.
656 QTimer::singleShot(1000, this, SLOT(synchronize()));
657 }
658
659 void DolphinColumnView::selectAll()
660 {
661 activeColumn()->selectAll();
662 }
663
664 bool DolphinColumnView::isIndexHidden(const QModelIndex& index) const
665 {
666 Q_UNUSED(index);
667 return false;//activeColumn()->isIndexHidden(index);
668 }
669
670 QModelIndex DolphinColumnView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
671 {
672 // Parts of this code have been taken from QColumnView::moveCursor().
673 // Copyright (C) 1992-2007 Trolltech ASA.
674
675 Q_UNUSED(modifiers);
676 if (model() == 0) {
677 return QModelIndex();
678 }
679
680 const QModelIndex current = currentIndex();
681 if (isRightToLeft()) {
682 if (cursorAction == MoveLeft) {
683 cursorAction = MoveRight;
684 } else if (cursorAction == MoveRight) {
685 cursorAction = MoveLeft;
686 }
687 }
688
689 switch (cursorAction) {
690 case MoveLeft:
691 if (m_index > 0) {
692 setActiveColumnIndex(m_index - 1);
693 }
694 break;
695
696 case MoveRight:
697 if (m_index < m_columns.count() - 1) {
698 setActiveColumnIndex(m_index + 1);
699 }
700 break;
701
702 default:
703 break;
704 }
705
706 return QModelIndex();
707 }
708
709 void DolphinColumnView::setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags flags)
710 {
711 Q_UNUSED(rect);
712 Q_UNUSED(flags);
713 //activeColumn()->setSelection(rect, flags);
714 }
715
716 QRegion DolphinColumnView::visualRegionForSelection(const QItemSelection& selection) const
717 {
718 Q_UNUSED(selection);
719 return QRegion(); //activeColumn()->visualRegionForSelection(selection);
720 }
721
722 int DolphinColumnView::horizontalOffset() const
723 {
724 return -m_contentX;
725 }
726
727 int DolphinColumnView::verticalOffset() const
728 {
729 return 0;
730 }
731
732 void DolphinColumnView::mousePressEvent(QMouseEvent* event)
733 {
734 m_controller->triggerActivation();
735 QAbstractItemView::mousePressEvent(event);
736 }
737
738 void DolphinColumnView::resizeEvent(QResizeEvent* event)
739 {
740 QAbstractItemView::resizeEvent(event);
741 layoutColumns();
742 updateScrollBar();
743 }
744
745 void DolphinColumnView::zoomIn()
746 {
747 if (isZoomInPossible()) {
748 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
749 // TODO: get rid of K3Icon sizes
750 switch (settings->iconSize()) {
751 case K3Icon::SizeSmall: settings->setIconSize(K3Icon::SizeMedium); break;
752 case K3Icon::SizeMedium: settings->setIconSize(K3Icon::SizeLarge); break;
753 default: Q_ASSERT(false); break;
754 }
755 updateDecorationSize();
756 }
757 }
758
759 void DolphinColumnView::zoomOut()
760 {
761 if (isZoomOutPossible()) {
762 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
763 // TODO: get rid of K3Icon sizes
764 switch (settings->iconSize()) {
765 case K3Icon::SizeLarge: settings->setIconSize(K3Icon::SizeMedium); break;
766 case K3Icon::SizeMedium: settings->setIconSize(K3Icon::SizeSmall); break;
767 default: Q_ASSERT(false); break;
768 }
769 updateDecorationSize();
770 }
771 }
772
773 void DolphinColumnView::moveContentHorizontally(int x)
774 {
775 m_contentX = -x;
776 layoutColumns();
777 }
778
779 void DolphinColumnView::updateDecorationSize()
780 {
781 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
782 const int iconSize = settings->iconSize();
783
784 foreach (QObject* object, viewport()->children()) {
785 if (object->inherits("QListView")) {
786 ColumnWidget* widget = static_cast<ColumnWidget*>(object);
787 widget->setDecorationSize(QSize(iconSize, iconSize));
788 }
789 }
790
791 m_controller->setZoomInPossible(isZoomInPossible());
792 m_controller->setZoomOutPossible(isZoomOutPossible());
793
794 doItemsLayout();
795 }
796
797 void DolphinColumnView::expandToActiveUrl()
798 {
799 disconnect(m_dolphinModel->dirLister(), SIGNAL(completed()),
800 this, SLOT(expandToActiveUrl()));
801
802 const int lastIndex = m_columns.count() - 1;
803 Q_ASSERT(lastIndex >= 0);
804 const KUrl& activeUrl = m_columns[lastIndex]->url();
805 const KUrl rootUrl = m_dolphinModel->dirLister()->url();
806 const bool expand = rootUrl.isParentOf(activeUrl)
807 && !rootUrl.equals(activeUrl, KUrl::CompareWithoutTrailingSlash);
808 if (expand) {
809 Q_ASSERT(m_dirListerCompleted);
810 m_dolphinModel->expandToUrl(activeUrl);
811 }
812 reloadColumns();
813 }
814
815 void DolphinColumnView::triggerReloadColumns(const QModelIndex& index)
816 {
817 Q_UNUSED(index);
818 // the reloading of the columns may not be done in the context of this slot
819 QMetaObject::invokeMethod(this, "reloadColumns", Qt::QueuedConnection);
820 }
821
822 void DolphinColumnView::reloadColumns()
823 {
824 const int end = m_columns.count() - 2; // next to last column
825 for (int i = 0; i <= end; ++i) {
826 ColumnWidget* nextColumn = m_columns[i + 1];
827
828 const QModelIndex rootIndex = nextColumn->rootIndex();
829 if (rootIndex.isValid()) {
830 nextColumn->show();
831 } else {
832 const QModelIndex dirIndex = m_dolphinModel->indexForUrl(m_columns[i]->childUrl());
833 const QModelIndex proxyIndex = m_proxyModel->mapFromSource(dirIndex);
834 if (proxyIndex.isValid()) {
835 nextColumn->setRootIndex(proxyIndex);
836 nextColumn->show();
837 if (nextColumn->isActive() && m_restoreActiveColumnFocus) {
838 nextColumn->setFocus();
839 m_restoreActiveColumnFocus = false;
840 }
841 }
842 }
843 }
844 assureVisibleActiveColumn();
845 }
846
847 void DolphinColumnView::synchronize()
848 {
849 if (m_dirListerCompleted) {
850 // expanding the active URL may only be done if the directory lister
851 // has been completed the loading
852 expandToActiveUrl();
853 } else {
854 reload();
855 }
856 }
857
858
859 void DolphinColumnView::slotDirListerStarted(const KUrl& url)
860 {
861 Q_UNUSED(url);
862 m_dirListerCompleted = false;
863 }
864
865 void DolphinColumnView::slotDirListerCompleted()
866 {
867 m_dirListerCompleted = true;
868 }
869
870 bool DolphinColumnView::isZoomInPossible() const
871 {
872 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
873 return settings->iconSize() < K3Icon::SizeLarge;
874 }
875
876 bool DolphinColumnView::isZoomOutPossible() const
877 {
878 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
879 return settings->iconSize() > K3Icon::SizeSmall;
880 }
881
882 void DolphinColumnView::setActiveColumnIndex(int index)
883 {
884 if (m_index == index) {
885 return;
886 }
887
888 const bool hasActiveColumn = (m_index >= 0);
889 if (hasActiveColumn) {
890 m_columns[m_index]->setActive(false);
891 }
892
893 m_index = index;
894 m_columns[m_index]->setActive(true);
895
896 m_controller->setUrl(m_columns[m_index]->url());
897 }
898
899 void DolphinColumnView::layoutColumns()
900 {
901 int x = m_contentX;
902 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
903 const int columnWidth = settings->columnWidth();
904 foreach (ColumnWidget* column, m_columns) {
905 column->setGeometry(QRect(x, 0, columnWidth, viewport()->height()));
906 x += columnWidth;
907 }
908 }
909
910 void DolphinColumnView::updateScrollBar()
911 {
912 int contentWidth = 0;
913 foreach (ColumnWidget* column, m_columns) {
914 contentWidth += column->width();
915 }
916
917 horizontalScrollBar()->setPageStep(contentWidth);
918 horizontalScrollBar()->setRange(0, contentWidth - viewport()->width());
919 }
920
921 void DolphinColumnView::assureVisibleActiveColumn()
922 {
923 const int viewportWidth = viewport()->width();
924 const int x = activeColumn()->x();
925 const int width = activeColumn()->width();
926 if (x + width > viewportWidth) {
927 int newContentX = m_contentX - x - width + viewportWidth;
928 if (newContentX > 0) {
929 newContentX = 0;
930 }
931 m_animation->setFrameRange(-m_contentX, -newContentX);
932 m_animation->start();
933 } else if (x < 0) {
934 const int newContentX = m_contentX - x;
935 m_animation->setFrameRange(-m_contentX, -newContentX);
936 m_animation->start();
937 }
938 }
939
940 void DolphinColumnView::requestActivation(ColumnWidget* column)
941 {
942 if (column->isActive()) {
943 assureVisibleActiveColumn();
944 } else {
945 int index = 0;
946 foreach (ColumnWidget* currColumn, m_columns) {
947 if (currColumn == column) {
948 setActiveColumnIndex(index);
949 assureVisibleActiveColumn();
950 return;
951 }
952 ++index;
953 }
954 }
955 }
956
957 #include "dolphincolumnview.moc"