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