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