]> cloud.milkyroute.net Git - dolphin.git/blob - src/views/dolphincolumnview.cpp
Use capitalized KDE includes
[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>
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
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 event->acceptProposedAction();
287 requestActivation();
288 DolphinTreeView::dragEnterEvent(event);
289 }
290
291 void DolphinColumnView::dragMoveEvent(QDragMoveEvent* event)
292 {
293 DolphinTreeView::dragMoveEvent(event);
294 event->acceptProposedAction();
295 }
296
297 void DolphinColumnView::dropEvent(QDropEvent* event)
298 {
299 const QModelIndex index = indexAt(event->pos());
300 m_container->m_dolphinViewController->setItemView(this);
301 const QModelIndex dolphinModelIndex = m_proxyModel->mapToSource(index);
302 const KFileItem item = m_dolphinModel->itemForIndex(dolphinModelIndex);
303 m_container->m_dolphinViewController->indicateDroppedUrls(item, event);
304 DolphinTreeView::dropEvent(event);
305 }
306
307 void DolphinColumnView::paintEvent(QPaintEvent* event)
308 {
309 if (!m_childUrl.isEmpty()) {
310 // Indicate the shown URL of the next column by highlighting the shown folder item
311 const QModelIndex dirIndex = m_dolphinModel->indexForUrl(m_childUrl);
312 const QModelIndex proxyIndex = m_proxyModel->mapFromSource(dirIndex);
313 if (proxyIndex.isValid() && !selectionModel()->isSelected(proxyIndex)) {
314 QPainter painter(viewport());
315
316 QStyleOptionViewItemV4 option;
317 option.initFrom(this);
318 option.rect = visualRect(proxyIndex);
319 option.state = QStyle::State_Enabled | QStyle::State_HasFocus;
320 option.viewItemPosition = QStyleOptionViewItemV4::OnlyOne;
321 style()->drawPrimitive(QStyle::PE_FrameFocusRect, &option, &painter, this);
322 }
323 }
324
325 DolphinTreeView::paintEvent(event);
326 }
327
328 void DolphinColumnView::mousePressEvent(QMouseEvent* event)
329 {
330 requestActivation();
331 if (!indexAt(event->pos()).isValid() && (QApplication::mouseButtons() & Qt::MidButton)) {
332 m_container->m_dolphinViewController->replaceUrlByClipboard();
333 }
334
335 DolphinTreeView::mousePressEvent(event);
336 }
337
338 void DolphinColumnView::keyPressEvent(QKeyEvent* event)
339 {
340 DolphinTreeView::keyPressEvent(event);
341
342 DolphinViewController* controller = m_container->m_dolphinViewController;
343 controller->handleKeyPressEvent(event);
344 switch (event->key()) {
345 case Qt::Key_Right: {
346 // Special key handling for the column: A Key_Right should
347 // open a new column for the currently selected folder.
348 const QModelIndex dolphinModelIndex = m_proxyModel->mapToSource(currentIndex());
349 const KFileItem item = m_dolphinModel->itemForIndex(dolphinModelIndex);
350 if (!item.isNull() && item.isDir()) {
351 controller->emitItemTriggered(item);
352 }
353 break;
354 }
355
356 case Qt::Key_Escape:
357 selectionModel()->setCurrentIndex(selectionModel()->currentIndex(),
358 QItemSelectionModel::Current |
359 QItemSelectionModel::Clear);
360 break;
361
362 default:
363 break;
364 }
365 }
366
367 void DolphinColumnView::contextMenuEvent(QContextMenuEvent* event)
368 {
369 requestActivation();
370 DolphinTreeView::contextMenuEvent(event);
371 m_container->m_dolphinViewController->triggerContextMenuRequest(event->pos());
372 }
373
374 void DolphinColumnView::wheelEvent(QWheelEvent* event)
375 {
376 const int step = m_decorationSize.height();
377 verticalScrollBar()->setSingleStep(step);
378 DolphinTreeView::wheelEvent(event);
379 }
380
381 void DolphinColumnView::leaveEvent(QEvent* event)
382 {
383 DolphinTreeView::leaveEvent(event);
384 // if the mouse is above an item and moved very fast outside the widget,
385 // no viewportEntered() signal might be emitted although the mouse has been moved
386 // above the viewport
387 m_container->m_dolphinViewController->emitViewportEntered();
388 }
389
390 void DolphinColumnView::currentChanged(const QModelIndex& current, const QModelIndex& previous)
391 {
392 DolphinTreeView::currentChanged(current, previous);
393 m_extensionsFactory->handleCurrentIndexChange(current, previous);
394 }
395
396 QRect DolphinColumnView::visualRect(const QModelIndex& index) const
397 {
398 QRect rect = DolphinTreeView::visualRect(index);
399
400 const QModelIndex dolphinModelIndex = m_proxyModel->mapToSource(index);
401 const KFileItem item = m_dolphinModel->itemForIndex(dolphinModelIndex);
402 if (!item.isNull()) {
403 const int width = DolphinFileItemDelegate::nameColumnWidth(item.text(), viewOptions());
404 rect.setWidth(width);
405 }
406
407 return rect;
408 }
409
410 bool DolphinColumnView::acceptsDrop(const QModelIndex& index) const
411 {
412 if (index.isValid() && (index.column() == DolphinModel::Name)) {
413 // Accept drops above directories
414 const QModelIndex dolphinModelIndex = m_proxyModel->mapToSource(index);
415 const KFileItem item = m_dolphinModel->itemForIndex(dolphinModelIndex);
416 return !item.isNull() && item.isDir();
417 }
418
419 return false;
420 }
421
422 bool DolphinColumnView::eventFilter(QObject* watched, QEvent* event)
423 {
424 if (watched == m_resizeWidget) {
425 switch (event->type()) {
426 case QEvent::MouseButtonPress: {
427 // Initiate the resizing of the column
428 QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
429 m_resizeXOrigin = mouseEvent->globalX();
430 m_resizeWidget->setMouseTracking(true);
431 event->accept();
432 return true;
433 }
434
435 case QEvent::MouseButtonDblClick: {
436 // Reset the column width to the default value
437 const ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
438 setMaximumWidth(settings->columnWidth());
439 m_container->layoutColumns();
440 m_resizeWidget->setMouseTracking(false);
441 m_resizeXOrigin = -1;
442 event->accept();
443 return true;
444 }
445
446 case QEvent::MouseMove: {
447 // Resize the column and trigger a relayout of the container
448 QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
449 int requestedWidth = maximumWidth() - m_resizeXOrigin + mouseEvent->globalX();;
450 if (requestedWidth < minimumWidth()) {
451 requestedWidth = minimumWidth();
452 }
453 setMaximumWidth(requestedWidth);
454
455 m_container->layoutColumns();
456
457 m_resizeXOrigin = mouseEvent->globalX();
458
459 event->accept();
460 return true;
461 }
462
463 case QEvent::MouseButtonRelease: {
464 // The resizing has been finished
465 m_resizeWidget->setMouseTracking(false);
466 m_resizeXOrigin = -1;
467 event->accept();
468 return true;
469 }
470
471 default:
472 break;
473 }
474 }
475 return DolphinTreeView::eventFilter(watched, event);
476 }
477 void DolphinColumnView::setZoomLevel(int level)
478 {
479 const int size = ZoomLevelInfo::iconSizeForZoomLevel(level);
480 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
481
482 const bool showPreview = m_container->m_dolphinViewController->view()->showPreview();
483 if (showPreview) {
484 settings->setPreviewSize(size);
485 } else {
486 settings->setIconSize(size);
487 }
488
489 updateDecorationSize(showPreview);
490 }
491
492 void DolphinColumnView::slotEntered(const QModelIndex& index)
493 {
494 m_container->m_dolphinViewController->setItemView(this);
495 m_container->m_dolphinViewController->emitItemEntered(index);
496 }
497
498 void DolphinColumnView::requestActivation()
499 {
500 m_container->m_dolphinViewController->requestActivation();
501 if (!m_active) {
502 m_container->requestActivation(this);
503 selectionModel()->clear();
504 }
505 }
506
507 void DolphinColumnView::updateFont()
508 {
509 const ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
510 Q_ASSERT(settings != 0);
511
512 if (settings->useSystemFont()) {
513 m_font = KGlobalSettings::generalFont();
514 }
515 }
516
517 void DolphinColumnView::slotShowPreviewChanged()
518 {
519 const DolphinView* view = m_container->m_dolphinViewController->view();
520 updateDecorationSize(view->showPreview());
521 }
522
523 void DolphinColumnView::slotDirListerCompleted()
524 {
525 if (!m_childUrl.isEmpty()) {
526 return;
527 }
528
529 // Try to optimize the width of the column, so that no name gets clipped
530 const int requiredWidth = sizeHintForColumn(DolphinModel::Name);
531
532 const ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
533 if (requiredWidth > settings->columnWidth()) {
534 int frameAroundContents = 0;
535 if (style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents)) {
536 // TODO: Using 2 PM_DefaultFrameWidths are not sufficient. Check Qt-code
537 // for other pixelmetrics that should be added...
538 frameAroundContents = style()->pixelMetric(QStyle::PM_DefaultFrameWidth) * 4;
539 }
540
541 const int scrollBarWidth = style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, verticalScrollBar());
542
543 setMaximumWidth(requiredWidth + frameAroundContents + scrollBarWidth);
544 m_container->layoutColumns();
545 if (m_active) {
546 m_container->assureVisibleActiveColumn();
547 }
548 }
549 }
550
551 void DolphinColumnView::activate()
552 {
553 setFocus(Qt::OtherFocusReason);
554
555 if (KGlobalSettings::singleClick()) {
556 connect(this, SIGNAL(clicked(const QModelIndex&)),
557 m_container->m_dolphinViewController, SLOT(triggerItem(const QModelIndex&)));
558 } else {
559 connect(this, SIGNAL(doubleClicked(const QModelIndex&)),
560 m_container->m_dolphinViewController, SLOT(triggerItem(const QModelIndex&)));
561 }
562
563 if (selectionModel() && selectionModel()->currentIndex().isValid()) {
564 selectionModel()->setCurrentIndex(selectionModel()->currentIndex(), QItemSelectionModel::SelectCurrent);
565 }
566
567 updateBackground();
568 }
569
570 void DolphinColumnView::deactivate()
571 {
572 clearFocus();
573 if (KGlobalSettings::singleClick()) {
574 disconnect(this, SIGNAL(clicked(const QModelIndex&)),
575 m_container->m_dolphinViewController, SLOT(triggerItem(const QModelIndex&)));
576 } else {
577 disconnect(this, SIGNAL(doubleClicked(const QModelIndex&)),
578 m_container->m_dolphinViewController, SLOT(triggerItem(const QModelIndex&)));
579 }
580
581 // It is important to disconnect the connection to requestActivation() temporary, otherwise the internal
582 // clearing of the selection would result in activating the column again.
583 disconnect(selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
584 this, SLOT(requestActivation()));
585 const QModelIndex current = selectionModel()->currentIndex();
586 selectionModel()->clear();
587 selectionModel()->setCurrentIndex(current, QItemSelectionModel::NoUpdate);
588 connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)),
589 this, SLOT(requestActivation()));
590
591 updateBackground();
592 }
593
594 void DolphinColumnView::updateDecorationSize(bool showPreview)
595 {
596 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
597 const int iconSize = showPreview ? settings->previewSize() : settings->iconSize();
598 const QSize size(iconSize, iconSize);
599 setIconSize(size);
600
601 m_decorationSize = size;
602
603 doItemsLayout();
604 }
605
606 #include "dolphincolumnview.moc"