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