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