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