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