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