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