]> cloud.milkyroute.net Git - dolphin.git/blob - src/dolphincolumnview.cpp
Refactored DolphinColumnWidget so that it does not need a hierarchical KDirLister...
[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 "dolphincolumnwidget.h"
24 #include "dolphincontroller.h"
25 #include "dolphindirlister.h"
26 #include "dolphinmodel.h"
27 #include "dolphinsortfilterproxymodel.h"
28 #include "dolphinsettings.h"
29
30 #include "dolphin_columnmodesettings.h"
31
32 #include <kcolorutils.h>
33 #include <kcolorscheme.h>
34 #include <kdirlister.h>
35
36 #include <QAbstractProxyModel>
37 #include <QApplication>
38 #include <QPoint>
39 #include <QScrollBar>
40 #include <QTimer>
41 #include <QTimeLine>
42
43 DolphinColumnView::DolphinColumnView(QWidget* parent, DolphinController* controller) :
44 QAbstractItemView(parent),
45 m_controller(controller),
46 m_restoreActiveColumnFocus(false),
47 m_index(-1),
48 m_contentX(0),
49 m_columns(),
50 m_animation(0),
51 m_dolphinModel(0),
52 m_proxyModel(0)
53 {
54 Q_ASSERT(controller != 0);
55
56 setAcceptDrops(true);
57 setDragDropMode(QAbstractItemView::DragDrop);
58 setDropIndicatorShown(false);
59 setSelectionMode(ExtendedSelection);
60
61 connect(this, SIGNAL(entered(const QModelIndex&)),
62 controller, SLOT(emitItemEntered(const QModelIndex&)));
63 connect(this, SIGNAL(viewportEntered()),
64 controller, SLOT(emitViewportEntered()));
65 connect(controller, SIGNAL(zoomIn()),
66 this, SLOT(zoomIn()));
67 connect(controller, SIGNAL(zoomOut()),
68 this, SLOT(zoomOut()));
69 connect(controller, SIGNAL(urlChanged(const KUrl&)),
70 this, SLOT(showColumn(const KUrl&)));
71 connect(controller, SIGNAL(showHiddenFilesChanged(bool)),
72 this, SLOT(slotShowHiddenFilesChanged(bool)));
73 connect(controller, SIGNAL(showPreviewChanged(bool)),
74 this, SLOT(slotShowPreviewChanged(bool)));
75
76 connect(horizontalScrollBar(), SIGNAL(valueChanged(int)),
77 this, SLOT(moveContentHorizontally(int)));
78
79 m_animation = new QTimeLine(500, this);
80 connect(m_animation, SIGNAL(frameChanged(int)), horizontalScrollBar(), SLOT(setValue(int)));
81
82 DolphinColumnWidget* column = new DolphinColumnWidget(viewport(), this, m_controller->url());
83 m_columns.append(column);
84 setActiveColumnIndex(0);
85
86 updateDecorationSize();
87
88 // dim the background of the viewport
89 QColor bgColor = KColorScheme(QPalette::Active, KColorScheme::View).background().color();
90 const QColor fgColor = KColorScheme(QPalette::Active, KColorScheme::View).foreground().color();
91 bgColor = KColorUtils::mix(bgColor, fgColor, 0.04);
92
93 QPalette palette = viewport()->palette();
94 palette.setColor(viewport()->backgroundRole(), bgColor);
95 viewport()->setPalette(palette);
96 }
97
98 DolphinColumnView::~DolphinColumnView()
99 {
100 }
101
102 QModelIndex DolphinColumnView::indexAt(const QPoint& point) const
103 {
104 foreach (DolphinColumnWidget* column, m_columns) {
105 const QPoint topLeft = column->frameGeometry().topLeft();
106 const QPoint adjustedPoint(point.x() - topLeft.x(), point.y() - topLeft.y());
107 const QModelIndex index = column->indexAt(adjustedPoint);
108 if (index.isValid()) {
109 return index;
110 }
111 }
112
113 return QModelIndex();
114 }
115
116 void DolphinColumnView::scrollTo(const QModelIndex& index, ScrollHint hint)
117 {
118 activeColumn()->scrollTo(index, hint);
119 }
120
121 QRect DolphinColumnView::visualRect(const QModelIndex& index) const
122 {
123 return activeColumn()->visualRect(index);
124 }
125
126 void DolphinColumnView::setModel(QAbstractItemModel* model)
127 {
128 m_proxyModel = static_cast<QAbstractProxyModel*>(model);
129 m_dolphinModel = static_cast<DolphinModel*>(m_proxyModel->sourceModel());
130 QAbstractItemView::setModel(model);
131 }
132
133 void DolphinColumnView::invertSelection()
134 {
135 QItemSelectionModel* selectionModel = activeColumn()->selectionModel();
136 const QAbstractItemModel* itemModel = selectionModel->model();
137
138 const QModelIndex topLeft = itemModel->index(0, 0);
139 const QModelIndex bottomRight = itemModel->index(itemModel->rowCount() - 1,
140 itemModel->columnCount() - 1);
141
142 const QItemSelection selection(topLeft, bottomRight);
143 selectionModel->select(selection, QItemSelectionModel::Toggle);
144 }
145
146 void DolphinColumnView::reload()
147 {
148 foreach (DolphinColumnWidget* column, m_columns) {
149 column->reload();
150 }
151 }
152
153 void DolphinColumnView::showColumn(const KUrl& url)
154 {
155 const KUrl& rootUrl = m_columns[0]->url();
156 if (!rootUrl.isParentOf(url)) {
157 // the URL is no child URL of the column view, hence clear all columns
158 // and reset the root column
159 QList<DolphinColumnWidget*>::iterator start = m_columns.begin() + 1;
160 QList<DolphinColumnWidget*>::iterator end = m_columns.end();
161 for (QList<DolphinColumnWidget*>::iterator it = start; it != end; ++it) {
162 (*it)->deleteLater();
163 }
164 m_columns.erase(start, end);
165 m_index = 0;
166 m_columns[0]->setActive(true);
167 m_columns[0]->setUrl(url);
168 assureVisibleActiveColumn();
169 return;
170 }
171
172 KDirLister* dirLister = m_dolphinModel->dirLister();
173 const KUrl dirListerUrl = dirLister->url();
174 if (dirListerUrl != rootUrl) {
175 // It is possible that root URL of the directory lister is adjusted
176 // after creating the column widget (e. g. when restoring the history
177 // having a different root URL than the controller indicates).
178 m_columns[0]->setUrl(dirListerUrl);
179 }
180
181 int columnIndex = 0;
182 foreach (DolphinColumnWidget* column, m_columns) {
183 if (column->url() == url) {
184 // the column represents already the requested URL, hence activate it
185 requestActivation(column);
186 return;
187 } else if (!column->url().isParentOf(url)) {
188 // the column is no parent of the requested URL, hence
189 // just delete all remaining columns
190 if (columnIndex > 0) {
191 QList<DolphinColumnWidget*>::iterator start = m_columns.begin() + columnIndex;
192 QList<DolphinColumnWidget*>::iterator end = m_columns.end();
193 for (QList<DolphinColumnWidget*>::iterator it = start; it != end; ++it) {
194 (*it)->deleteLater();
195 }
196 m_columns.erase(start, end);
197
198 const int maxIndex = m_columns.count() - 1;
199 Q_ASSERT(maxIndex >= 0);
200 if (m_index > maxIndex) {
201 m_index = maxIndex;
202 }
203 break;
204 }
205 }
206 ++columnIndex;
207 }
208
209 // Create missing columns. Assuming that the path is "/home/peter/Temp/" and
210 // the target path is "/home/peter/Temp/a/b/c/", then the columns "a", "b" and
211 // "c" will be created.
212 const int lastIndex = m_columns.count() - 1;
213 Q_ASSERT(lastIndex >= 0);
214
215 const KUrl& activeUrl = m_columns[lastIndex]->url();
216 Q_ASSERT(activeUrl.isParentOf(url));
217 Q_ASSERT(activeUrl != url);
218
219 QString path = activeUrl.url(KUrl::AddTrailingSlash);
220 const QString targetPath = url.url(KUrl::AddTrailingSlash);
221
222 columnIndex = lastIndex;
223 int slashIndex = path.count('/');
224 bool hasSubPath = (slashIndex >= 0);
225 while (hasSubPath) {
226 const QString subPath = targetPath.section('/', slashIndex, slashIndex);
227 if (subPath.isEmpty()) {
228 hasSubPath = false;
229 } else {
230 path += subPath + '/';
231 ++slashIndex;
232
233 const KUrl childUrl = KUrl(path);
234 const QModelIndex dirIndex = m_dolphinModel->indexForUrl(KUrl(path));
235 const QModelIndex proxyIndex = m_proxyModel->mapFromSource(dirIndex);
236
237 m_columns[columnIndex]->setChildUrl(childUrl);
238 columnIndex++;
239
240 DolphinColumnWidget* column = new DolphinColumnWidget(viewport(), this, childUrl);
241 column->setActive(false);
242
243 m_columns.append(column);
244
245 // Before invoking layoutColumns() the column must be set visible temporary.
246 // To prevent a flickering the initial geometry is set to a hidden position.
247 column->setGeometry(QRect(-1, -1, 1, 1));
248 column->show();
249 layoutColumns();
250 updateScrollBar();
251
252 // the layout is finished, now let the column be invisible until it
253 // gets a valid root index due to expandToActiveUrl()
254 //column->hide();
255 }
256 }
257
258 // set the last column as active column without modifying the controller
259 // and hence the history
260 activeColumn()->setActive(false);
261 m_index = columnIndex;
262 activeColumn()->setActive(true);
263 assureVisibleActiveColumn();
264 }
265
266 void DolphinColumnView::selectAll()
267 {
268 activeColumn()->selectAll();
269 }
270
271 bool DolphinColumnView::isIndexHidden(const QModelIndex& index) const
272 {
273 Q_UNUSED(index);
274 return false;//activeColumn()->isIndexHidden(index);
275 }
276
277 QModelIndex DolphinColumnView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
278 {
279 // Parts of this code have been taken from QColumnView::moveCursor().
280 // Copyright (C) 1992-2007 Trolltech ASA.
281
282 Q_UNUSED(modifiers);
283 if (model() == 0) {
284 return QModelIndex();
285 }
286
287 const QModelIndex current = currentIndex();
288 if (isRightToLeft()) {
289 if (cursorAction == MoveLeft) {
290 cursorAction = MoveRight;
291 } else if (cursorAction == MoveRight) {
292 cursorAction = MoveLeft;
293 }
294 }
295
296 switch (cursorAction) {
297 case MoveLeft:
298 if (m_index > 0) {
299 setActiveColumnIndex(m_index - 1);
300 }
301 break;
302
303 case MoveRight:
304 if (m_index < m_columns.count() - 1) {
305 setActiveColumnIndex(m_index + 1);
306 }
307 break;
308
309 default:
310 break;
311 }
312
313 return QModelIndex();
314 }
315
316 void DolphinColumnView::setSelection(const QRect& rect, QItemSelectionModel::SelectionFlags flags)
317 {
318 Q_UNUSED(rect);
319 Q_UNUSED(flags);
320 //activeColumn()->setSelection(rect, flags);
321 }
322
323 QRegion DolphinColumnView::visualRegionForSelection(const QItemSelection& selection) const
324 {
325 Q_UNUSED(selection);
326 return QRegion(); //activeColumn()->visualRegionForSelection(selection);
327 }
328
329 int DolphinColumnView::horizontalOffset() const
330 {
331 return -m_contentX;
332 }
333
334 int DolphinColumnView::verticalOffset() const
335 {
336 return 0;
337 }
338
339 void DolphinColumnView::mousePressEvent(QMouseEvent* event)
340 {
341 m_controller->triggerActivation();
342 QAbstractItemView::mousePressEvent(event);
343 }
344
345 void DolphinColumnView::resizeEvent(QResizeEvent* event)
346 {
347 QAbstractItemView::resizeEvent(event);
348 layoutColumns();
349 updateScrollBar();
350 }
351
352 void DolphinColumnView::zoomIn()
353 {
354 if (isZoomInPossible()) {
355 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
356 switch (settings->iconSize()) {
357 case KIconLoader::SizeSmall: settings->setIconSize(KIconLoader::SizeMedium); break;
358 case KIconLoader::SizeMedium: settings->setIconSize(KIconLoader::SizeLarge); break;
359 default: Q_ASSERT(false); break;
360 }
361 updateDecorationSize();
362 }
363 }
364
365 void DolphinColumnView::zoomOut()
366 {
367 if (isZoomOutPossible()) {
368 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
369 switch (settings->iconSize()) {
370 case KIconLoader::SizeLarge: settings->setIconSize(KIconLoader::SizeMedium); break;
371 case KIconLoader::SizeMedium: settings->setIconSize(KIconLoader::SizeSmall); break;
372 default: Q_ASSERT(false); break;
373 }
374 updateDecorationSize();
375 }
376 }
377
378 void DolphinColumnView::moveContentHorizontally(int x)
379 {
380 m_contentX = isRightToLeft() ? +x : -x;
381 layoutColumns();
382 }
383
384 void DolphinColumnView::updateDecorationSize()
385 {
386 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
387 const int iconSize = settings->iconSize();
388
389 foreach (QObject* object, viewport()->children()) {
390 if (object->inherits("QListView")) {
391 DolphinColumnWidget* widget = static_cast<DolphinColumnWidget*>(object);
392 widget->setDecorationSize(QSize(iconSize, iconSize));
393 }
394 }
395
396 m_controller->setZoomInPossible(isZoomInPossible());
397 m_controller->setZoomOutPossible(isZoomOutPossible());
398
399 doItemsLayout();
400 }
401
402 void DolphinColumnView::slotShowHiddenFilesChanged(bool show)
403 {
404 foreach (DolphinColumnWidget* column, m_columns) {
405 column->setShowHiddenFiles(show);
406 }
407 }
408
409 void DolphinColumnView::slotShowPreviewChanged(bool show)
410 {
411 foreach (DolphinColumnWidget* column, m_columns) {
412 column->setShowPreview(show);
413 }
414 }
415
416 bool DolphinColumnView::isZoomInPossible() const
417 {
418 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
419 return settings->iconSize() < KIconLoader::SizeLarge;
420 }
421
422 bool DolphinColumnView::isZoomOutPossible() const
423 {
424 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
425 return settings->iconSize() > KIconLoader::SizeSmall;
426 }
427
428 void DolphinColumnView::setActiveColumnIndex(int index)
429 {
430 if (m_index == index) {
431 return;
432 }
433
434 const bool hasActiveColumn = (m_index >= 0);
435 if (hasActiveColumn) {
436 m_columns[m_index]->setActive(false);
437 }
438
439 m_index = index;
440 m_columns[m_index]->setActive(true);
441
442 m_controller->setUrl(m_columns[m_index]->url());
443
444 assureVisibleActiveColumn();
445 }
446
447 void DolphinColumnView::layoutColumns()
448 {
449 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
450 const int columnWidth = settings->columnWidth();
451 if (isRightToLeft()) {
452 int x = viewport()->width() - columnWidth + m_contentX;
453 foreach (DolphinColumnWidget* column, m_columns) {
454 column->setGeometry(QRect(x, 0, columnWidth, viewport()->height()));
455 x -= columnWidth;
456 }
457 } else {
458 int x = m_contentX;
459 foreach (DolphinColumnWidget* column, m_columns) {
460 column->setGeometry(QRect(x, 0, columnWidth, viewport()->height()));
461 x += columnWidth;
462 }
463 }
464 }
465
466 void DolphinColumnView::updateScrollBar()
467 {
468 int contentWidth = 0;
469 foreach (DolphinColumnWidget* column, m_columns) {
470 contentWidth += column->width();
471 }
472
473 horizontalScrollBar()->setPageStep(contentWidth);
474 horizontalScrollBar()->setRange(0, contentWidth - viewport()->width());
475 }
476
477 void DolphinColumnView::assureVisibleActiveColumn()
478 {
479 const int viewportWidth = viewport()->width();
480 const int x = activeColumn()->x();
481 const int width = activeColumn()->width();
482 if (x + width > viewportWidth) {
483 const int newContentX = m_contentX - x - width + viewportWidth;
484 if (isRightToLeft()) {
485 m_animation->setFrameRange(m_contentX, newContentX);
486 } else {
487 m_animation->setFrameRange(-m_contentX, -newContentX);
488 }
489 m_animation->start();
490 } else if (x < 0) {
491 const int newContentX = m_contentX - x;
492 if (isRightToLeft()) {
493 m_animation->setFrameRange(m_contentX, newContentX);
494 } else {
495 m_animation->setFrameRange(-m_contentX, -newContentX);
496 }
497 m_animation->start();
498 }
499 }
500
501 void DolphinColumnView::requestActivation(DolphinColumnWidget* column)
502 {
503 if (column->isActive()) {
504 assureVisibleActiveColumn();
505 } else {
506 int index = 0;
507 foreach (DolphinColumnWidget* currColumn, m_columns) {
508 if (currColumn == column) {
509 setActiveColumnIndex(index);
510 return;
511 }
512 ++index;
513 }
514 }
515 }
516
517 #include "dolphincolumnview.moc"