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