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