]> cloud.milkyroute.net Git - dolphin.git/blob - src/views/dolphincolumnview.cpp
Fix Dolphin session management regression
[dolphin.git] / src / views / dolphincolumnview.cpp
1 /***************************************************************************
2 * Copyright (C) 2007-2009 by Peter Penz <peter.penz19@gmail.com> *
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 "dolphincolumnviewcontainer.h"
24 #include "dolphinviewcontroller.h"
25 #include "dolphindirlister.h"
26 #include "dolphinfileitemdelegate.h"
27 #include "dolphinsortfilterproxymodel.h"
28 #include "settings/dolphinsettings.h"
29 #include "dolphinviewautoscroller.h"
30 #include "dolphin_columnmodesettings.h"
31 #include "dolphin_generalsettings.h"
32 #include "draganddrophelper.h"
33 #include "folderexpander.h"
34 #include "tooltips/tooltipmanager.h"
35 #include "viewextensionsfactory.h"
36 #include "viewmodecontroller.h"
37 #include "zoomlevelinfo.h"
38
39 #include <KColorScheme>
40 #include <KDirLister>
41 #include <KFileItem>
42 #include <KIO/PreviewJob>
43 #include <KIcon>
44 #include <KIconEffect>
45 #include <KJob>
46 #include <KLocale>
47 #include <konqmimedata.h>
48
49 #include <QApplication>
50 #include <QClipboard>
51 #include <QHeaderView>
52 #include <QLabel>
53 #include <QPainter>
54 #include <QPoint>
55 #include <QScrollBar>
56
57 DolphinColumnView::DolphinColumnView(QWidget* parent,
58 DolphinColumnViewContainer* container,
59 const KUrl& url) :
60 DolphinTreeView(parent),
61 m_active(false),
62 m_container(container),
63 m_extensionsFactory(0),
64 m_url(url),
65 m_childUrl(),
66 m_font(),
67 m_decorationSize(),
68 m_dirLister(0),
69 m_dolphinModel(0),
70 m_proxyModel(0),
71 m_resizeWidget(0),
72 m_resizeXOrigin(-1)
73 {
74 setMouseTracking(true);
75 setAcceptDrops(true);
76 setUniformRowHeights(true);
77 setSelectionBehavior(SelectItems);
78 setSelectionMode(QAbstractItemView::ExtendedSelection);
79 setDragDropMode(QAbstractItemView::DragDrop);
80 setDropIndicatorShown(false);
81 setRootIsDecorated(false);
82 setItemsExpandable(false);
83 setEditTriggers(QAbstractItemView::NoEditTriggers);
84 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
85 setVerticalScrollMode(QTreeView::ScrollPerPixel);
86
87 m_resizeWidget = new QLabel(this);
88 m_resizeWidget->setPixmap(KIcon("transform-move").pixmap(KIconLoader::SizeSmall));
89 m_resizeWidget->setToolTip(i18nc("@info:tooltip", "Resize column"));
90 setCornerWidget(m_resizeWidget);
91 m_resizeWidget->installEventFilter(this);
92
93 const ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
94 Q_ASSERT(settings);
95
96 if (settings->useSystemFont()) {
97 m_font = KGlobalSettings::generalFont();
98 } else {
99 m_font = QFont(settings->fontFamily(),
100 qRound(settings->fontSize()),
101 settings->fontWeight(),
102 settings->italicFont());
103 m_font.setPointSizeF(settings->fontSize());
104 }
105
106 setMinimumWidth(settings->fontSize() * 10);
107 setMaximumWidth(settings->columnWidth());
108
109 connect(this, SIGNAL(viewportEntered()),
110 m_container->m_dolphinViewController, SLOT(emitViewportEntered()));
111 connect(this, SIGNAL(entered(const QModelIndex&)),
112 this, SLOT(slotEntered(const QModelIndex&)));
113
114 const DolphinView* dolphinView = m_container->m_dolphinViewController->view();
115 connect(dolphinView, SIGNAL(showPreviewChanged()),
116 this, SLOT(slotShowPreviewChanged()));
117
118 m_dirLister = new DolphinDirLister();
119 m_dirLister->setAutoUpdate(true);
120 m_dirLister->setMainWindow(window());
121 m_dirLister->setDelayedMimeTypes(true);
122 const bool showHiddenFiles = m_container->m_dolphinViewController->view()->showHiddenFiles();
123 m_dirLister->setShowingDotFiles(showHiddenFiles);
124 connect(m_dirLister, SIGNAL(completed()), this, SLOT(slotDirListerCompleted()));
125
126 m_dolphinModel = new DolphinModel(this);
127 m_dolphinModel->setDirLister(m_dirLister);
128 m_dolphinModel->setDropsAllowed(DolphinModel::DropOnDirectory);
129
130 m_proxyModel = new DolphinSortFilterProxyModel(this);
131 m_proxyModel->setSourceModel(m_dolphinModel);
132 m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
133
134 m_proxyModel->setSorting(dolphinView->sorting());
135 m_proxyModel->setSortOrder(dolphinView->sortOrder());
136 m_proxyModel->setSortFoldersFirst(dolphinView->sortFoldersFirst());
137
138 setModel(m_proxyModel);
139
140 connect(KGlobalSettings::self(), SIGNAL(kdisplayFontChanged()),
141 this, SLOT(updateFont()));
142
143 const ViewModeController* viewModeController = m_container->m_viewModeController;
144 connect(viewModeController, SIGNAL(zoomLevelChanged(int)),
145 this, SLOT(setZoomLevel(int)));
146 const QString nameFilter = viewModeController->nameFilter();
147 if (!nameFilter.isEmpty()) {
148 m_proxyModel->setFilterFixedString(nameFilter);
149 }
150
151 updateDecorationSize(dolphinView->showPreview());
152 updateBackground();
153
154 DolphinViewController* dolphinViewController = m_container->m_dolphinViewController;
155 m_extensionsFactory = new ViewExtensionsFactory(this, dolphinViewController, viewModeController);
156 m_extensionsFactory->fileItemDelegate()->setMinimizedNameColumn(true);
157
158 m_dirLister->openUrl(url, KDirLister::NoFlags);
159 }
160
161 DolphinColumnView::~DolphinColumnView()
162 {
163 delete m_proxyModel;
164 m_proxyModel = 0;
165 delete m_dolphinModel;
166 m_dolphinModel = 0;
167 m_dirLister = 0; // deleted by m_dolphinModel
168 }
169
170
171 void DolphinColumnView::setActive(bool active)
172 {
173 if (m_active != active) {
174 m_active = active;
175
176 if (active) {
177 activate();
178 } else {
179 deactivate();
180 }
181 }
182 }
183
184 bool DolphinColumnView::isActive() const
185 {
186 return m_active;
187 }
188
189 void DolphinColumnView::setChildUrl(const KUrl& url)
190 {
191 m_childUrl = url;
192 }
193
194 KUrl DolphinColumnView::childUrl() const
195 {
196 return m_childUrl;
197 }
198
199 void DolphinColumnView::setUrl(const KUrl& url)
200 {
201 if (url != m_url) {
202 m_url = url;
203 m_dirLister->openUrl(url, KDirLister::NoFlags);
204 }
205 }
206
207 KUrl DolphinColumnView::url() const
208 {
209 return m_url;
210 }
211
212 void DolphinColumnView::updateBackground()
213 {
214 // TODO: The alpha-value 150 is copied from DolphinView::setActive(). When
215 // cleaning up the cut-indication of DolphinColumnView with the code from
216 // DolphinView a common helper-class should be available which can be shared
217 // by all view implementations -> no hardcoded value anymore
218 const QPalette::ColorRole role = viewport()->backgroundRole();
219 QColor color = viewport()->palette().color(role);
220 color.setAlpha((m_active && m_container->m_active) ? 255 : 150);
221
222 QPalette palette = viewport()->palette();
223 palette.setColor(role, color);
224 viewport()->setPalette(palette);
225
226 update();
227 }
228
229 KFileItem DolphinColumnView::itemAt(const QPoint& pos) const
230 {
231 KFileItem item;
232 const QModelIndex index = indexAt(pos);
233 if (index.isValid() && (index.column() == DolphinModel::Name)) {
234 const QModelIndex dolphinModelIndex = m_proxyModel->mapToSource(index);
235 item = m_dolphinModel->itemForIndex(dolphinModelIndex);
236 }
237 return item;
238 }
239
240 void DolphinColumnView::setSelectionModel(QItemSelectionModel* model)
241 {
242 // If a change of the selection is done although the view is not active
243 // (e. g. by the selection markers), the column must be activated. This
244 // is done by listening to the current selectionChanged() signal.
245 if (selectionModel()) {
246 disconnect(selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
247 this, SLOT(requestActivation()));
248 }
249
250 DolphinTreeView::setSelectionModel(model);
251
252 connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
253 this, SLOT(requestActivation()));
254 }
255
256 QStyleOptionViewItem DolphinColumnView::viewOptions() const
257 {
258 QStyleOptionViewItem viewOptions = DolphinTreeView::viewOptions();
259 viewOptions.font = m_font;
260 viewOptions.fontMetrics = QFontMetrics(m_font);
261 viewOptions.decorationSize = m_decorationSize;
262 viewOptions.showDecorationSelected = true;
263 return viewOptions;
264 }
265
266 bool DolphinColumnView::event(QEvent* event)
267 {
268 if (event->type() == QEvent::Polish) {
269 // Hide all columns except of the 'Name' column
270 for (int i = DolphinModel::Name + 1; i < DolphinModel::ExtraColumnCount; ++i) {
271 hideColumn(i);
272 }
273 header()->hide();
274 }
275
276 return DolphinTreeView::event(event);
277 }
278
279 void DolphinColumnView::startDrag(Qt::DropActions supportedActions)
280 {
281 DragAndDropHelper::instance().startDrag(this, supportedActions, m_container->m_dolphinViewController);
282 DolphinTreeView::startDrag(supportedActions);
283 }
284
285 void DolphinColumnView::dragEnterEvent(QDragEnterEvent* event)
286 {
287 event->acceptProposedAction();
288 requestActivation();
289 DolphinTreeView::dragEnterEvent(event);
290 }
291
292 void DolphinColumnView::dragMoveEvent(QDragMoveEvent* event)
293 {
294 DolphinTreeView::dragMoveEvent(event);
295 event->acceptProposedAction();
296 }
297
298 void DolphinColumnView::dropEvent(QDropEvent* event)
299 {
300 const QModelIndex index = indexAt(event->pos());
301 m_container->m_dolphinViewController->setItemView(this);
302 const QModelIndex dolphinModelIndex = m_proxyModel->mapToSource(index);
303 const KFileItem item = m_dolphinModel->itemForIndex(dolphinModelIndex);
304 m_container->m_dolphinViewController->indicateDroppedUrls(item, event);
305 DolphinTreeView::dropEvent(event);
306 }
307
308 void DolphinColumnView::paintEvent(QPaintEvent* event)
309 {
310 if (!m_childUrl.isEmpty()) {
311 // Indicate the shown URL of the next column by highlighting the shown folder item
312 const QModelIndex dirIndex = m_dolphinModel->indexForUrl(m_childUrl);
313 const QModelIndex proxyIndex = m_proxyModel->mapFromSource(dirIndex);
314 if (proxyIndex.isValid() && !selectionModel()->isSelected(proxyIndex)) {
315 QPainter painter(viewport());
316
317 QStyleOptionViewItemV4 option;
318 option.initFrom(this);
319 option.rect = visualRect(proxyIndex);
320 option.state = QStyle::State_Enabled | QStyle::State_HasFocus;
321 option.viewItemPosition = QStyleOptionViewItemV4::OnlyOne;
322 style()->drawPrimitive(QStyle::PE_FrameFocusRect, &option, &painter, this);
323 }
324 }
325
326 DolphinTreeView::paintEvent(event);
327 }
328
329 void DolphinColumnView::mousePressEvent(QMouseEvent* event)
330 {
331 requestActivation();
332 if (!indexAt(event->pos()).isValid() && (QApplication::mouseButtons() & Qt::MidButton)) {
333 m_container->m_dolphinViewController->replaceUrlByClipboard();
334 }
335
336 DolphinTreeView::mousePressEvent(event);
337 }
338
339 void DolphinColumnView::keyPressEvent(QKeyEvent* event)
340 {
341 const bool hadSelection = selectionModel()->hasSelection();
342 DolphinTreeView::keyPressEvent(event);
343
344 DolphinViewController* controller = m_container->m_dolphinViewController;
345 controller->handleKeyPressEvent(event);
346 switch (event->key()) {
347 case Qt::Key_Right: {
348 // Special key handling for the column: A Key_Right should
349 // open a new column for the currently selected folder.
350 QModelIndex dolphinModelIndex = m_proxyModel->mapToSource(currentIndex());
351
352 // If there is no selection we automatically move to the child url
353 // instead of the first directory.
354 // See BUG:263110
355 if (!hadSelection && !childUrl().isEmpty()) {
356 dolphinModelIndex = m_dolphinModel->indexForUrl(childUrl());
357 }
358
359 const KFileItem item = m_dolphinModel->itemForIndex(dolphinModelIndex);
360 if (!item.isNull() && item.isDir()) {
361 controller->emitItemTriggered(item);
362 }
363 break;
364 }
365
366 case Qt::Key_Escape:
367 selectionModel()->setCurrentIndex(selectionModel()->currentIndex(),
368 QItemSelectionModel::Current |
369 QItemSelectionModel::Clear);
370 break;
371
372 default:
373 break;
374 }
375 }
376
377 void DolphinColumnView::contextMenuEvent(QContextMenuEvent* event)
378 {
379 requestActivation();
380 DolphinTreeView::contextMenuEvent(event);
381 m_container->m_dolphinViewController->triggerContextMenuRequest(event->pos());
382 }
383
384 void DolphinColumnView::wheelEvent(QWheelEvent* event)
385 {
386 const int step = m_decorationSize.height();
387 verticalScrollBar()->setSingleStep(step);
388 DolphinTreeView::wheelEvent(event);
389 }
390
391 void DolphinColumnView::leaveEvent(QEvent* event)
392 {
393 DolphinTreeView::leaveEvent(event);
394 // if the mouse is above an item and moved very fast outside the widget,
395 // no viewportEntered() signal might be emitted although the mouse has been moved
396 // above the viewport
397 m_container->m_dolphinViewController->emitViewportEntered();
398 }
399
400 void DolphinColumnView::currentChanged(const QModelIndex& current, const QModelIndex& previous)
401 {
402 DolphinTreeView::currentChanged(current, previous);
403 m_extensionsFactory->handleCurrentIndexChange(current, previous);
404 }
405
406 QRect DolphinColumnView::visualRect(const QModelIndex& index) const
407 {
408 QRect rect = DolphinTreeView::visualRect(index);
409
410 const QModelIndex dolphinModelIndex = m_proxyModel->mapToSource(index);
411 const KFileItem item = m_dolphinModel->itemForIndex(dolphinModelIndex);
412 if (!item.isNull()) {
413 const int width = DolphinFileItemDelegate::nameColumnWidth(item.text(), viewOptions());
414 rect.setWidth(width);
415 }
416
417 return rect;
418 }
419
420 bool DolphinColumnView::acceptsDrop(const QModelIndex& index) const
421 {
422 if (index.isValid() && (index.column() == DolphinModel::Name)) {
423 // Accept drops above directories
424 const QModelIndex dolphinModelIndex = m_proxyModel->mapToSource(index);
425 const KFileItem item = m_dolphinModel->itemForIndex(dolphinModelIndex);
426 return !item.isNull() && item.isDir();
427 }
428
429 return false;
430 }
431
432 bool DolphinColumnView::eventFilter(QObject* watched, QEvent* event)
433 {
434 if (watched == m_resizeWidget) {
435 switch (event->type()) {
436 case QEvent::MouseButtonPress: {
437 // Initiate the resizing of the column
438 QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
439 m_resizeXOrigin = mouseEvent->globalX();
440 m_resizeWidget->setMouseTracking(true);
441 event->accept();
442 return true;
443 }
444
445 case QEvent::MouseButtonDblClick: {
446 // Reset the column width to the default value
447 const ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
448 setMaximumWidth(settings->columnWidth());
449 m_container->layoutColumns();
450 m_resizeWidget->setMouseTracking(false);
451 m_resizeXOrigin = -1;
452 event->accept();
453 return true;
454 }
455
456 case QEvent::MouseMove: {
457 // Resize the column and trigger a relayout of the container
458 QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
459 int requestedWidth = maximumWidth() - m_resizeXOrigin + mouseEvent->globalX();;
460 if (requestedWidth < minimumWidth()) {
461 requestedWidth = minimumWidth();
462 }
463 setMaximumWidth(requestedWidth);
464
465 m_container->layoutColumns();
466
467 m_resizeXOrigin = mouseEvent->globalX();
468
469 event->accept();
470 return true;
471 }
472
473 case QEvent::MouseButtonRelease: {
474 // The resizing has been finished
475 m_resizeWidget->setMouseTracking(false);
476 m_resizeXOrigin = -1;
477 event->accept();
478 return true;
479 }
480
481 default:
482 break;
483 }
484 }
485 return DolphinTreeView::eventFilter(watched, event);
486 }
487 void DolphinColumnView::setZoomLevel(int level)
488 {
489 const int size = ZoomLevelInfo::iconSizeForZoomLevel(level);
490 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
491
492 const bool showPreview = m_container->m_dolphinViewController->view()->showPreview();
493 if (showPreview) {
494 settings->setPreviewSize(size);
495 } else {
496 settings->setIconSize(size);
497 }
498
499 updateDecorationSize(showPreview);
500 }
501
502 void DolphinColumnView::slotEntered(const QModelIndex& index)
503 {
504 m_container->m_dolphinViewController->setItemView(this);
505 m_container->m_dolphinViewController->emitItemEntered(index);
506 }
507
508 void DolphinColumnView::requestActivation()
509 {
510 m_container->m_dolphinViewController->requestActivation();
511 if (!m_active) {
512 m_container->requestActivation(this);
513 selectionModel()->clear();
514 }
515 }
516
517 void DolphinColumnView::updateFont()
518 {
519 const ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
520 Q_ASSERT(settings);
521
522 if (settings->useSystemFont()) {
523 m_font = KGlobalSettings::generalFont();
524 }
525 }
526
527 void DolphinColumnView::slotShowPreviewChanged()
528 {
529 const DolphinView* view = m_container->m_dolphinViewController->view();
530 updateDecorationSize(view->showPreview());
531 }
532
533 void DolphinColumnView::slotDirListerCompleted()
534 {
535 if (!m_childUrl.isEmpty()) {
536 return;
537 }
538
539 // Try to optimize the width of the column, so that no name gets clipped
540 const int requiredWidth = sizeHintForColumn(DolphinModel::Name);
541
542 const ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
543 if (requiredWidth > settings->columnWidth()) {
544 int frameAroundContents = 0;
545 if (style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents)) {
546 // TODO: Using 2 PM_DefaultFrameWidths are not sufficient. Check Qt-code
547 // for other pixelmetrics that should be added...
548 frameAroundContents = style()->pixelMetric(QStyle::PM_DefaultFrameWidth) * 4;
549 }
550
551 const int scrollBarWidth = style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, verticalScrollBar());
552
553 setMaximumWidth(requiredWidth + frameAroundContents + scrollBarWidth);
554 m_container->layoutColumns();
555 if (m_active) {
556 m_container->assureVisibleActiveColumn();
557 }
558 }
559 }
560
561 void DolphinColumnView::activate()
562 {
563 setFocus(Qt::OtherFocusReason);
564
565 connect(this, SIGNAL(clicked(const QModelIndex&)),
566 m_container->m_dolphinViewController, SLOT(requestTab(const QModelIndex&)));
567 if (KGlobalSettings::singleClick()) {
568 connect(this, SIGNAL(clicked(const QModelIndex&)),
569 m_container->m_dolphinViewController, SLOT(triggerItem(const QModelIndex&)));
570 } else {
571 connect(this, SIGNAL(doubleClicked(const QModelIndex&)),
572 m_container->m_dolphinViewController, SLOT(triggerItem(const QModelIndex&)));
573 }
574
575 if (selectionModel() && selectionModel()->currentIndex().isValid()) {
576 selectionModel()->setCurrentIndex(selectionModel()->currentIndex(), QItemSelectionModel::SelectCurrent);
577 }
578
579 updateBackground();
580 }
581
582 void DolphinColumnView::deactivate()
583 {
584 clearFocus();
585
586 disconnect(this, SIGNAL(clicked(const QModelIndex&)),
587 m_container->m_dolphinViewController, SLOT(requestTab(const QModelIndex&)));
588 if (KGlobalSettings::singleClick()) {
589 disconnect(this, SIGNAL(clicked(const QModelIndex&)),
590 m_container->m_dolphinViewController, SLOT(triggerItem(const QModelIndex&)));
591 } else {
592 disconnect(this, SIGNAL(doubleClicked(const QModelIndex&)),
593 m_container->m_dolphinViewController, SLOT(triggerItem(const QModelIndex&)));
594 }
595
596 // It is important to disconnect the connection to requestActivation() temporary, otherwise the internal
597 // clearing of the selection would result in activating the column again.
598 disconnect(selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
599 this, SLOT(requestActivation()));
600 const QModelIndex current = selectionModel()->currentIndex();
601 selectionModel()->clear();
602 selectionModel()->setCurrentIndex(current, QItemSelectionModel::NoUpdate);
603 connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
604 this, SLOT(requestActivation()));
605
606 updateBackground();
607 }
608
609 void DolphinColumnView::updateDecorationSize(bool showPreview)
610 {
611 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
612 const int iconSize = showPreview ? settings->previewSize() : settings->iconSize();
613 const QSize size(iconSize, iconSize);
614 setIconSize(size);
615
616 m_decorationSize = size;
617
618 doItemsLayout();
619 }
620
621 #include "dolphincolumnview.moc"