]> cloud.milkyroute.net Git - dolphin.git/blob - src/dolphincolumnviewcontainer.cpp
Fix wrong description: The selection is meant for the information panel, not the...
[dolphin.git] / src / dolphincolumnviewcontainer.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 "dolphincolumnviewcontainer.h"
21
22 #include "dolphin_columnmodesettings.h"
23
24 #include "dolphincolumnview.h"
25 #include "dolphinviewcontroller.h"
26 #include "dolphinsortfilterproxymodel.h"
27 #include "draganddrophelper.h"
28 #include "settings/dolphinsettings.h"
29 #include "viewmodecontroller.h"
30
31 #include <QPoint>
32 #include <QScrollBar>
33 #include <QTimeLine>
34 #include <QTimer>
35
36 DolphinColumnViewContainer::DolphinColumnViewContainer(QWidget* parent,
37 DolphinViewController* dolphinViewController,
38 const ViewModeController* viewModeController) :
39 QScrollArea(parent),
40 m_dolphinViewController(dolphinViewController),
41 m_viewModeController(viewModeController),
42 m_active(false),
43 m_index(-1),
44 m_contentX(0),
45 m_columns(),
46 m_emptyViewport(0),
47 m_animation(0),
48 m_dragSource(0),
49 m_activeUrlTimer(0)
50 {
51 Q_ASSERT(dolphinViewController != 0);
52 Q_ASSERT(viewModeController != 0);
53
54 setAcceptDrops(true);
55 setFocusPolicy(Qt::NoFocus);
56 setFrameShape(QFrame::NoFrame);
57 setLayoutDirection(Qt::LeftToRight);
58
59 connect(viewModeController, SIGNAL(activationChanged(bool)),
60 this, SLOT(updateColumnsBackground(bool)));
61
62 connect(horizontalScrollBar(), SIGNAL(valueChanged(int)),
63 this, SLOT(moveContentHorizontally(int)));
64
65 m_animation = new QTimeLine(500, this);
66 connect(m_animation, SIGNAL(frameChanged(int)), horizontalScrollBar(), SLOT(setValue(int)));
67
68 m_activeUrlTimer = new QTimer(this);
69 m_activeUrlTimer->setSingleShot(true);
70 m_activeUrlTimer->setInterval(200);
71 connect(m_activeUrlTimer, SIGNAL(timeout()),
72 this, SLOT(updateActiveUrl()));
73
74 DolphinColumnView* column = new DolphinColumnView(viewport(), this, viewModeController->url());
75 m_columns.append(column);
76 setActiveColumnIndex(0);
77
78 m_emptyViewport = new QFrame(viewport());
79 m_emptyViewport->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
80
81 updateColumnsBackground(true);
82
83 }
84
85 DolphinColumnViewContainer::~DolphinColumnViewContainer()
86 {
87 delete m_dragSource;
88 m_dragSource = 0;
89 }
90
91 KUrl DolphinColumnViewContainer::rootUrl() const
92 {
93 return m_columns[0]->url();
94 }
95
96 QAbstractItemView* DolphinColumnViewContainer::activeColumn() const
97 {
98 return m_columns[m_index];
99 }
100
101 void DolphinColumnViewContainer::showColumn(const KUrl& url)
102 {
103 if (!rootUrl().isParentOf(url)) {
104 removeAllColumns();
105 m_columns[0]->setUrl(url);
106 return;
107 }
108
109 int columnIndex = 0;
110 foreach (DolphinColumnView* column, m_columns) {
111 if (column->url() == url) {
112 // the column represents already the requested URL, hence activate it
113 requestActivation(column);
114 layoutColumns();
115 return;
116 } else if (!column->url().isParentOf(url)) {
117 // the column is no parent of the requested URL, hence
118 // just delete all remaining columns
119 if (columnIndex > 0) {
120 QList<DolphinColumnView*>::iterator start = m_columns.begin() + columnIndex;
121 QList<DolphinColumnView*>::iterator end = m_columns.end();
122 for (QList<DolphinColumnView*>::iterator it = start; it != end; ++it) {
123 deleteColumn(*it);
124 }
125 m_columns.erase(start, end);
126
127 const int maxIndex = m_columns.count() - 1;
128 Q_ASSERT(maxIndex >= 0);
129 if (m_index > maxIndex) {
130 m_index = maxIndex;
131 }
132 break;
133 }
134 }
135 ++columnIndex;
136 }
137
138 // Create missing columns. Assuming that the path is "/home/peter/Temp/" and
139 // the target path is "/home/peter/Temp/a/b/c/", then the columns "a", "b" and
140 // "c" will be created.
141 const int lastIndex = m_columns.count() - 1;
142 Q_ASSERT(lastIndex >= 0);
143
144 const KUrl& activeUrl = m_columns[lastIndex]->url();
145 Q_ASSERT(activeUrl.isParentOf(url));
146 Q_ASSERT(activeUrl != url);
147
148 QString path = activeUrl.url(KUrl::AddTrailingSlash);
149 const QString targetPath = url.url(KUrl::AddTrailingSlash);
150
151 columnIndex = lastIndex;
152 int slashIndex = path.count('/');
153 bool hasSubPath = (slashIndex >= 0);
154 while (hasSubPath) {
155 const QString subPath = targetPath.section('/', slashIndex, slashIndex);
156 if (subPath.isEmpty()) {
157 hasSubPath = false;
158 } else {
159 path += subPath + '/';
160 ++slashIndex;
161
162 const KUrl childUrl = KUrl(path);
163 m_columns[columnIndex]->setChildUrl(childUrl);
164 columnIndex++;
165
166 DolphinColumnView* column = new DolphinColumnView(viewport(), this, childUrl);
167 m_columns.append(column);
168
169 // Before invoking layoutColumns() the column must be set visible temporary.
170 // To prevent a flickering the initial geometry is set to a hidden position.
171 column->setGeometry(QRect(-1, -1, 1, 1));
172 column->show();
173 layoutColumns();
174 updateScrollBar();
175 }
176 }
177
178 // set the last column as active column without modifying the controller
179 // and hence the history
180 m_columns[m_index]->setActive(false);
181 m_index = columnIndex;
182 m_columns[m_index]->setActive(true);
183 assureVisibleActiveColumn();
184 }
185
186 void DolphinColumnViewContainer::mousePressEvent(QMouseEvent* event)
187 {
188 m_dolphinViewController->requestActivation();
189 QScrollArea::mousePressEvent(event);
190 }
191
192 void DolphinColumnViewContainer::keyPressEvent(QKeyEvent* event)
193 {
194 if (event->key() == Qt::Key_Left) {
195 setActiveColumnIndex(m_index - 1);
196 } else {
197 QScrollArea::keyPressEvent(event);
198 }
199 }
200
201 void DolphinColumnViewContainer::resizeEvent(QResizeEvent* event)
202 {
203 QScrollArea::resizeEvent(event);
204 layoutColumns();
205 updateScrollBar();
206 assureVisibleActiveColumn();
207 }
208
209 void DolphinColumnViewContainer::wheelEvent(QWheelEvent* event)
210 {
211 // let Ctrl+wheel events propagate to the DolphinView for icon zooming
212 if ((event->modifiers() & Qt::ControlModifier) == Qt::ControlModifier) {
213 event->ignore();
214 } else {
215 QScrollArea::wheelEvent(event);
216 }
217 }
218
219 void DolphinColumnViewContainer::moveContentHorizontally(int x)
220 {
221 m_contentX = isRightToLeft() ? +x : -x;
222 layoutColumns();
223 }
224
225 void DolphinColumnViewContainer::updateColumnsBackground(bool active)
226 {
227 if (active == m_active) {
228 return;
229 }
230
231 m_active = active;
232
233 // dim the background of the viewport
234 const QPalette::ColorRole role = viewport()->backgroundRole();
235 QColor background = viewport()->palette().color(role);
236 background.setAlpha(0); // make background transparent
237
238 QPalette palette = viewport()->palette();
239 palette.setColor(role, background);
240 viewport()->setPalette(palette);
241
242 foreach (DolphinColumnView* column, m_columns) {
243 column->updateBackground();
244 }
245 }
246
247 void DolphinColumnViewContainer::updateActiveUrl()
248 {
249 const KUrl activeUrl = m_columns[m_index]->url();
250 m_dolphinViewController->requestUrlChange(activeUrl);
251 }
252
253 void DolphinColumnViewContainer::setActiveColumnIndex(int index)
254 {
255 if ((m_index == index) || (index < 0) || (index >= m_columns.count())) {
256 return;
257 }
258
259 const bool hasActiveColumn = (m_index >= 0);
260 if (hasActiveColumn) {
261 m_columns[m_index]->setActive(false);
262 }
263
264 m_index = index;
265 m_columns[m_index]->setActive(true);
266
267 assureVisibleActiveColumn();
268 m_activeUrlTimer->start(); // calls slot updateActiveUrl()
269 }
270
271 void DolphinColumnViewContainer::layoutColumns()
272 {
273 const int gap = 4;
274
275 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
276 const int columnWidth = settings->columnWidth();
277
278 QRect emptyViewportRect;
279 if (isRightToLeft()) {
280 int x = viewport()->width() - columnWidth + m_contentX;
281 foreach (DolphinColumnView* column, m_columns) {
282 column->setGeometry(QRect(x, 0, columnWidth - gap, viewport()->height()));
283 x -= columnWidth;
284 }
285 emptyViewportRect = QRect(0, 0, x + columnWidth - gap, viewport()->height());
286 } else {
287 int x = m_contentX;
288 foreach (DolphinColumnView* column, m_columns) {
289 column->setGeometry(QRect(x, 0, columnWidth - gap, viewport()->height()));
290 x += columnWidth;
291 }
292 emptyViewportRect = QRect(x, 0, viewport()->width() - x - gap, viewport()->height());
293 }
294
295 if (emptyViewportRect.isValid()) {
296 m_emptyViewport->show();
297 m_emptyViewport->setGeometry(emptyViewportRect);
298 } else {
299 m_emptyViewport->hide();
300 }
301 }
302
303 void DolphinColumnViewContainer::updateScrollBar()
304 {
305 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
306 const int contentWidth = m_columns.count() * settings->columnWidth();
307
308 horizontalScrollBar()->setPageStep(contentWidth);
309 horizontalScrollBar()->setRange(0, contentWidth - viewport()->width());
310 }
311
312 void DolphinColumnViewContainer::assureVisibleActiveColumn()
313 {
314 const int viewportWidth = viewport()->width();
315 const int x = activeColumn()->x();
316
317 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
318 const int width = settings->columnWidth();
319
320 if (x + width > viewportWidth) {
321 const int newContentX = m_contentX - x - width + viewportWidth;
322 if (isRightToLeft()) {
323 m_animation->setFrameRange(m_contentX, newContentX);
324 } else {
325 m_animation->setFrameRange(-m_contentX, -newContentX);
326 }
327 if (m_animation->state() != QTimeLine::Running) {
328 m_animation->start();
329 }
330 } else if (x < 0) {
331 const int newContentX = m_contentX - x;
332 if (isRightToLeft()) {
333 m_animation->setFrameRange(m_contentX, newContentX);
334 } else {
335 m_animation->setFrameRange(-m_contentX, -newContentX);
336 }
337 if (m_animation->state() != QTimeLine::Running) {
338 m_animation->start();
339 }
340 }
341 }
342
343 void DolphinColumnViewContainer::requestActivation(DolphinColumnView* column)
344 {
345 m_dolphinViewController->setItemView(column);
346 if (column->isActive()) {
347 assureVisibleActiveColumn();
348 } else {
349 int index = 0;
350 foreach (DolphinColumnView* currColumn, m_columns) {
351 if (currColumn == column) {
352 setActiveColumnIndex(index);
353 return;
354 }
355 ++index;
356 }
357 }
358 }
359
360 void DolphinColumnViewContainer::removeAllColumns()
361 {
362 QList<DolphinColumnView*>::iterator start = m_columns.begin() + 1;
363 QList<DolphinColumnView*>::iterator end = m_columns.end();
364 for (QList<DolphinColumnView*>::iterator it = start; it != end; ++it) {
365 deleteColumn(*it);
366 }
367 m_columns.erase(start, end);
368 m_index = 0;
369 m_columns[0]->setActive(true);
370 assureVisibleActiveColumn();
371 }
372
373 QPoint DolphinColumnViewContainer::columnPosition(DolphinColumnView* column, const QPoint& point) const
374 {
375 const QPoint topLeft = column->frameGeometry().topLeft();
376 return QPoint(point.x() - topLeft.x(), point.y() - topLeft.y());
377 }
378
379 void DolphinColumnViewContainer::deleteColumn(DolphinColumnView* column)
380 {
381 if (column == 0) {
382 return;
383 }
384
385 if (m_dolphinViewController->itemView() == column) {
386 m_dolphinViewController->setItemView(0);
387 }
388 // deleteWhenNotDragSource(column) does not necessarily delete column,
389 // and we want its preview generator destroyed immediately.
390 column->hide();
391 // Prevent automatic destruction of column when this DolphinColumnViewContainer
392 // is destroyed.
393 column->setParent(0);
394 column->disconnect();
395
396 if (DragAndDropHelper::instance().isDragSource(column)) {
397 // The column is a drag source (the feature "Open folders
398 // during drag operations" is used). Deleting the view
399 // during an ongoing drag operation is not allowed, so
400 // this will postponed.
401 if (m_dragSource != 0) {
402 // the old stored view is obviously not the drag source anymore
403 m_dragSource->deleteLater();
404 m_dragSource = 0;
405 }
406 column->hide();
407 column->setParent(0);
408 column->disconnect();
409
410 m_dragSource = column;
411 } else {
412 column->deleteLater();
413 }
414 }
415
416 #include "dolphincolumnviewcontainer.moc"