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