]> cloud.milkyroute.net Git - dolphin.git/blob - src/dolphincolumnviewcontainer.cpp
fix link issue if Nepomuk is not installed
[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 m_columns.append(column);
164
165 // Before invoking layoutColumns() the column must be set visible temporary.
166 // To prevent a flickering the initial geometry is set to a hidden position.
167 column->setGeometry(QRect(-1, -1, 1, 1));
168 column->show();
169 layoutColumns();
170 updateScrollBar();
171 }
172 }
173
174 // set the last column as active column without modifying the controller
175 // and hence the history
176 m_columns[m_index]->setActive(false);
177 m_index = columnIndex;
178 m_columns[m_index]->setActive(true);
179 assureVisibleActiveColumn();
180 }
181
182 void DolphinColumnViewContainer::mousePressEvent(QMouseEvent* event)
183 {
184 m_controller->requestActivation();
185 QScrollArea::mousePressEvent(event);
186 }
187
188 void DolphinColumnViewContainer::keyPressEvent(QKeyEvent* event)
189 {
190 if (event->key() == Qt::Key_Left) {
191 setActiveColumnIndex(m_index - 1);
192 } else {
193 QScrollArea::keyPressEvent(event);
194 }
195 }
196
197 void DolphinColumnViewContainer::resizeEvent(QResizeEvent* event)
198 {
199 QScrollArea::resizeEvent(event);
200 layoutColumns();
201 updateScrollBar();
202 assureVisibleActiveColumn();
203 }
204
205 void DolphinColumnViewContainer::wheelEvent(QWheelEvent* event)
206 {
207 // let Ctrl+wheel events propagate to the DolphinView for icon zooming
208 if ((event->modifiers() & Qt::ControlModifier) == Qt::ControlModifier) {
209 event->ignore();
210 } else {
211 QScrollArea::wheelEvent(event);
212 }
213 }
214
215 void DolphinColumnViewContainer::moveContentHorizontally(int x)
216 {
217 m_contentX = isRightToLeft() ? +x : -x;
218 layoutColumns();
219 }
220
221 void DolphinColumnViewContainer::updateColumnsBackground(bool active)
222 {
223 if (active == m_active) {
224 return;
225 }
226
227 m_active = active;
228
229 // dim the background of the viewport
230 const QPalette::ColorRole role = viewport()->backgroundRole();
231 QColor background = viewport()->palette().color(role);
232 background.setAlpha(0); // make background transparent
233
234 QPalette palette = viewport()->palette();
235 palette.setColor(role, background);
236 viewport()->setPalette(palette);
237
238 foreach (DolphinColumnView* column, m_columns) {
239 column->updateBackground();
240 }
241 }
242
243 void DolphinColumnViewContainer::updateActiveUrl()
244 {
245 const KUrl activeUrl = m_columns[m_index]->url();
246 m_controller->setUrl(activeUrl);
247 }
248
249 void DolphinColumnViewContainer::setActiveColumnIndex(int index)
250 {
251 if ((m_index == index) || (index < 0) || (index >= m_columns.count())) {
252 return;
253 }
254
255 const bool hasActiveColumn = (m_index >= 0);
256 if (hasActiveColumn) {
257 m_columns[m_index]->setActive(false);
258 }
259
260 m_index = index;
261 m_columns[m_index]->setActive(true);
262
263 assureVisibleActiveColumn();
264 m_activeUrlTimer->start(); // calls slot updateActiveUrl()
265 }
266
267 void DolphinColumnViewContainer::layoutColumns()
268 {
269 const int gap = 4;
270
271 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
272 const int columnWidth = settings->columnWidth();
273
274 QRect emptyViewportRect;
275 if (isRightToLeft()) {
276 int x = viewport()->width() - columnWidth + m_contentX;
277 foreach (DolphinColumnView* column, m_columns) {
278 column->setGeometry(QRect(x, 0, columnWidth - gap, viewport()->height()));
279 x -= columnWidth;
280 }
281 emptyViewportRect = QRect(0, 0, x + columnWidth - gap, viewport()->height());
282 } else {
283 int x = m_contentX;
284 foreach (DolphinColumnView* column, m_columns) {
285 column->setGeometry(QRect(x, 0, columnWidth - gap, viewport()->height()));
286 x += columnWidth;
287 }
288 emptyViewportRect = QRect(x, 0, viewport()->width() - x - gap, viewport()->height());
289 }
290
291 if (emptyViewportRect.isValid()) {
292 m_emptyViewport->show();
293 m_emptyViewport->setGeometry(emptyViewportRect);
294 } else {
295 m_emptyViewport->hide();
296 }
297 }
298
299 void DolphinColumnViewContainer::updateScrollBar()
300 {
301 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
302 const int contentWidth = m_columns.count() * settings->columnWidth();
303
304 horizontalScrollBar()->setPageStep(contentWidth);
305 horizontalScrollBar()->setRange(0, contentWidth - viewport()->width());
306 }
307
308 void DolphinColumnViewContainer::assureVisibleActiveColumn()
309 {
310 const int viewportWidth = viewport()->width();
311 const int x = activeColumn()->x();
312
313 ColumnModeSettings* settings = DolphinSettings::instance().columnModeSettings();
314 const int width = settings->columnWidth();
315
316 if (x + width > viewportWidth) {
317 const int newContentX = m_contentX - x - width + viewportWidth;
318 if (isRightToLeft()) {
319 m_animation->setFrameRange(m_contentX, newContentX);
320 } else {
321 m_animation->setFrameRange(-m_contentX, -newContentX);
322 }
323 if (m_animation->state() != QTimeLine::Running) {
324 m_animation->start();
325 }
326 } else if (x < 0) {
327 const int newContentX = m_contentX - x;
328 if (isRightToLeft()) {
329 m_animation->setFrameRange(m_contentX, newContentX);
330 } else {
331 m_animation->setFrameRange(-m_contentX, -newContentX);
332 }
333 if (m_animation->state() != QTimeLine::Running) {
334 m_animation->start();
335 }
336 }
337 }
338
339 void DolphinColumnViewContainer::requestActivation(DolphinColumnView* column)
340 {
341 m_controller->setItemView(column);
342 if (column->isActive()) {
343 assureVisibleActiveColumn();
344 } else {
345 int index = 0;
346 foreach (DolphinColumnView* currColumn, m_columns) {
347 if (currColumn == column) {
348 setActiveColumnIndex(index);
349 return;
350 }
351 ++index;
352 }
353 }
354 }
355
356 void DolphinColumnViewContainer::removeAllColumns()
357 {
358 QList<DolphinColumnView*>::iterator start = m_columns.begin() + 1;
359 QList<DolphinColumnView*>::iterator end = m_columns.end();
360 for (QList<DolphinColumnView*>::iterator it = start; it != end; ++it) {
361 deleteColumn(*it);
362 }
363 m_columns.erase(start, end);
364 m_index = 0;
365 m_columns[0]->setActive(true);
366 assureVisibleActiveColumn();
367 }
368
369 QPoint DolphinColumnViewContainer::columnPosition(DolphinColumnView* column, const QPoint& point) const
370 {
371 const QPoint topLeft = column->frameGeometry().topLeft();
372 return QPoint(point.x() - topLeft.x(), point.y() - topLeft.y());
373 }
374
375 void DolphinColumnViewContainer::deleteColumn(DolphinColumnView* column)
376 {
377 if (column == 0) {
378 return;
379 }
380
381 if (m_controller->itemView() == column) {
382 m_controller->setItemView(0);
383 }
384 // deleteWhenNotDragSource(column) does not necessarily delete column,
385 // and we want its preview generator destroyed immediately.
386 column->hide();
387 // Prevent automatic destruction of column when this DolphinColumnViewContainer
388 // is destroyed.
389 column->setParent(0);
390 column->disconnect();
391
392 if (DragAndDropHelper::instance().isDragSource(column)) {
393 // The column is a drag source (the feature "Open folders
394 // during drag operations" is used). Deleting the view
395 // during an ongoing drag operation is not allowed, so
396 // this will postponed.
397 if (m_dragSource != 0) {
398 // the old stored view is obviously not the drag source anymore
399 m_dragSource->deleteLater();
400 m_dragSource = 0;
401 }
402 column->hide();
403 column->setParent(0);
404 column->disconnect();
405
406 m_dragSource = column;
407 } else {
408 column->deleteLater();
409 }
410 }
411
412 #include "dolphincolumnviewcontainer.moc"