only include Applications by default on OS X
[ambit.git] / src / qsidebar.cpp
blob71ab1d8c0f001d122a135f713dbc4ca2630ca538
1 /****************************************************************************
2 **
3 ** Copyright (C) 1992-2007 Trolltech ASA. All rights reserved.
4 **
5 ** This file is part of the QtGui module of the Qt Toolkit.
6 **
7 ** This file may be used under the terms of the GNU General Public
8 ** License version 2.0 as published by the Free Software Foundation
9 ** and appearing in the file LICENSE.GPL included in the packaging of
10 ** this file. Please review the following information to ensure GNU
11 ** General Public Licensing requirements will be met:
12 ** http://www.trolltech.com/products/qt/opensource.html
14 ** If you are unsure which license is appropriate for your use, please
15 ** review the following information:
16 ** http://www.trolltech.com/products/qt/licensing.html or contact the
17 ** sales department at sales@trolltech.com.
19 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
20 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22 ****************************************************************************/
24 #include "qsidebar.h"
25 #include "qfilesystemmodel_p.h"
27 #ifndef QT_NO_FILEDIALOG
29 #include <qaction.h>
30 #include <qurl.h>
31 #include <qmenu.h>
32 #include <qmimedata.h>
33 #include <qevent.h>
34 #include <qdebug.h>
35 #if QT_VERSION >= 0x040300
36 #include <qfileiconprovider.h>
37 #else
38 #include <qdirmodel.h>
39 #endif
41 /*!
42 QUrlModel lets you have indexes from a QFileSystemModel to a list. When QFileSystemModel
43 changes them QUrlModel will automatically update.
45 Example usage: File dialog sidebar and combo box
47 QUrlModel::QUrlModel(QObject *parent) : QStandardItemModel(parent), fileSystemModel(0)
51 /*!
52 \reimp
54 QStringList QUrlModel::mimeTypes() const
56 return QStringList(QLatin1String("text/uri-list"));
59 /*!
60 \reimp
62 Qt::ItemFlags QUrlModel::flags(const QModelIndex &index) const
64 Qt::ItemFlags flags = QStandardItemModel::flags(index);
65 if (index.isValid()) {
66 flags &= ~Qt::ItemIsEditable;
67 // ### some future version could support "moving" urls onto a folder
68 flags &= ~Qt::ItemIsDropEnabled;
71 if (index.data(Qt::DecorationRole).isNull())
72 flags &= ~Qt::ItemIsEnabled;
74 if (invalidUrls.contains(index.data(UrlRole).toUrl()))
75 flags &= ~Qt::ItemIsEnabled;
77 return flags;
80 /*!
81 \reimp
83 QMimeData *QUrlModel::mimeData(const QModelIndexList &indexes) const
85 QList<QUrl> list;
86 for (int i = 0; i < indexes.count(); ++i) {
87 if (indexes.at(i).column() == 0)
88 list.append(indexes.at(i).data(UrlRole).toUrl());
90 QMimeData *data = new QMimeData();
91 data->setUrls(list);
92 return data;
95 /*!
96 Decide based upon the data if it should be accepted or not
98 We only accept dirs and not files
100 bool QUrlModel::canDrop(QDragEnterEvent *event)
102 if (!event->mimeData()->formats().contains(mimeTypes().first()))
103 return false;
105 const QList<QUrl> list = event->mimeData()->urls();
106 for (int i = 0; i < list.count(); ++i) {
107 QModelIndex idx = fileSystemModel->index(list.at(0).toLocalFile());
108 if (!fileSystemModel->isDir(idx))
109 return false;
111 return true;
115 \reimp
117 bool QUrlModel::dropMimeData(const QMimeData *data, Qt::DropAction action,
118 int row, int column, const QModelIndex &parent)
120 if (!data->formats().contains(mimeTypes().first()))
121 return false;
122 Q_UNUSED(action);
123 Q_UNUSED(column);
124 Q_UNUSED(parent);
125 addUrls(data->urls(), row);
126 return true;
130 \reimp
132 If the role is the UrlRole then handle otherwise just pass to QStandardItemModel
134 bool QUrlModel::setData(const QModelIndex &index, const QVariant &value, int role)
136 if (value.type() == QVariant::Url) {
137 QUrl url = value.toUrl();
138 QModelIndex dirIndex = fileSystemModel->index(url.path());
139 QStandardItemModel::setData(index, fileSystemModel->data(dirIndex).toString());
140 QStandardItemModel::setData(index, fileSystemModel->data(dirIndex, Qt::DecorationRole),
141 Qt::DecorationRole);
142 QStandardItemModel::setData(index, url, UrlRole);
143 return true;
145 return QStandardItemModel::setData(index, value, role);
148 void QUrlModel::setUrl(const QModelIndex &index, const QUrl &url, const QModelIndex &dirIndex)
150 setData(index, url, UrlRole);
151 if (url.path().isEmpty()) {
152 setData(index, fileSystemModel->myComputer());
153 setData(index, fileSystemModel->myComputer(Qt::DecorationRole), Qt::DecorationRole);
154 } else {
155 QString newName = dirIndex.data().toString();
156 QIcon newIcon = qvariant_cast<QIcon>(dirIndex.data(Qt::DecorationRole));
157 if (!dirIndex.isValid()) {
158 newIcon = fileSystemModel->iconProvider()->icon(QFileIconProvider::Folder);
159 newName = QFileInfo(url.toLocalFile()).fileName();
160 if (!invalidUrls.contains(url))
161 invalidUrls.append(url);
164 // Make sure that we have at least 32x32 images
165 const QSize size = newIcon.actualSize(QSize(32,32));
166 if (size.width() < 32) {
167 QPixmap smallPixmap = newIcon.pixmap(QSize(32, 32));
168 newIcon.addPixmap(smallPixmap.scaledToWidth(32, Qt::SmoothTransformation));
171 if (index.data().toString() != newName)
172 setData(index, newName);
173 QIcon oldIcon = qvariant_cast<QIcon>(index.data(Qt::DecorationRole));
174 // if (oldIcon.cacheKey() != newIcon.cacheKey())
175 setData(index, newIcon, Qt::DecorationRole);
179 void QUrlModel::setUrls(const QList<QUrl> &list)
181 removeRows(0, rowCount());
182 invalidUrls.clear();
183 watching.clear();
184 addUrls(list, 0);
188 Add urls \a list into the list at \a row. If move then movie
189 existing ones to row.
191 \sa dropMimeData()
193 void QUrlModel::addUrls(const QList<QUrl> &list, int row, bool move)
195 if (row == -1)
196 row = rowCount();
197 row = qMin(row, rowCount());
198 for (int i = list.count() - 1; i >= 0; --i) {
199 QUrl url = list.at(i);
200 if (!url.isValid() || url.scheme() != QLatin1String("file"))
201 continue;
202 for (int j = 0; move && j < rowCount(); ++j) {
203 if (index(j, 0).data(UrlRole) == url) {
204 removeRow(j);
205 if (j <= row)
206 row--;
207 break;
210 row = qMax(row, 0);
211 QModelIndex idx = fileSystemModel->index(url.toLocalFile());
212 if (!fileSystemModel->isDir(idx))
213 continue;
214 insertRows(row, 1);
215 setUrl(index(row, 0), url, idx);
216 watching.append(QPair<QModelIndex, QString>(idx, url.toLocalFile()));
221 Return the complete list of urls in a QList.
223 QList<QUrl> QUrlModel::urls() const
225 QList<QUrl> list;
226 for (int i = 0; i < rowCount(); ++i)
227 list.append(data(index(i, 0), UrlRole).toUrl());
228 return list;
232 QFileSystemModel to get index's from, clears existing rows
234 void QUrlModel::setFileSystemModel(QFileSystemModel *model)
236 if (model == fileSystemModel)
237 return;
238 if (fileSystemModel != 0) {
239 disconnect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
240 this, SLOT(dataChanged(const QModelIndex &, const QModelIndex &)));
241 disconnect(model, SIGNAL(layoutChanged()),
242 this, SLOT(layoutChanged()));
244 fileSystemModel = model;
245 if (fileSystemModel != 0) {
246 connect(model, SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
247 this, SLOT(dataChanged(const QModelIndex &, const QModelIndex &)));
248 connect(model, SIGNAL(layoutChanged()),
249 this, SLOT(layoutChanged()));
251 clear();
252 insertColumns(0, 1);
256 If one of the index's we are watching has changed update our internal data
258 void QUrlModel::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
260 QModelIndex parent = topLeft.parent();
261 for (int i = 0; i < watching.count(); ++i) {
262 QModelIndex index = watching.at(i).first;
263 if (index.model() && topLeft.model()) {
264 Q_ASSERT(index.model() == topLeft.model());
266 if ( index.row() >= topLeft.row()
267 && index.row() <= bottomRight.row()
268 && index.column() >= topLeft.column()
269 && index.column() <= bottomRight.column()
270 && index.parent() == parent) {
271 changed(watching.at(i).second);
277 Re-get all of our data, anything could have changed!
279 void QUrlModel::layoutChanged()
281 QStringList paths;
282 for (int i = 0; i < watching.count(); ++i)
283 paths.append(watching.at(i).second);
284 watching.clear();
285 for (int i = 0; i < paths.count(); ++i) {
286 QString path = paths.at(i);
287 QModelIndex newIndex = fileSystemModel->index(path);
288 watching.append(QPair<QModelIndex, QString>(newIndex, path));
289 if (newIndex.isValid())
290 changed(path);
295 The following path changed data update our copy of that data
297 \sa layoutChanged() dataChanged()
299 void QUrlModel::changed(const QString &path)
301 for (int i = 0; i < rowCount(); ++i) {
302 QModelIndex idx = index(i, 0);
303 if (idx.data(UrlRole).toUrl().toLocalFile() == path) {
304 setData(idx, idx.data(UrlRole).toUrl());
309 QSidebar::QSidebar(QFileSystemModel *model, const QList<QUrl> &newUrls, QWidget *parent) : QListView(parent)
311 // ### TODO make icon size dynamic
312 setIconSize(QSize(32,32));
313 setUniformItemSizes(true);
314 urlModel = new QUrlModel(this);
315 urlModel->setFileSystemModel(model);
316 setModel(urlModel);
318 connect(selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
319 this, SLOT(clicked(const QModelIndex &)));
320 setDragDropMode(QAbstractItemView::DragDrop);
321 setContextMenuPolicy(Qt::CustomContextMenu);
322 connect(this, SIGNAL(customContextMenuRequested(const QPoint &)),
323 this, SLOT(showContextMenu(const QPoint &)));
324 urlModel->setUrls(newUrls);
325 setCurrentIndex(this->model()->index(0,0));
328 QSidebar::~QSidebar()
332 void QSidebar::dragEnterEvent(QDragEnterEvent *event)
334 if (urlModel->canDrop(event))
335 QListView::dragEnterEvent(event);
338 QSize QSidebar::sizeHint() const
340 int w = 0;
341 int h = 0;
342 for (int i = 0; i < model()->rowCount(); ++i) {
343 QSize s = QListView::sizeHintForIndex(model()->index(0, 0));
344 w = qMax(w, s.width());
345 h = qMax(h, s.width());
347 return QSize(w + 64,h);
350 void QSidebar::selectIndex(const QModelIndex &index) {
351 selectUrl(QUrl::fromLocalFile(index.data(QFileSystemModel::FilePathRole).toString()));
354 void QSidebar::selectUrl(const QUrl &url)
356 disconnect(selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
357 this, SLOT(clicked(const QModelIndex &)));
359 selectionModel()->clear();
360 for (int i = 0; i < model()->rowCount(); ++i) {
361 if (model()->index(i, 0).data(QUrlModel::UrlRole).toUrl() == url) {
362 selectionModel()->select(model()->index(i, 0), QItemSelectionModel::Select);
363 break;
367 connect(selectionModel(), SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
368 this, SLOT(clicked(const QModelIndex &)));
372 \internal
374 \sa removeEntry()
376 void QSidebar::showContextMenu(const QPoint &position)
378 QList<QAction *> actions;
379 if (indexAt(position).isValid()) {
380 QAction *action = new QAction(tr("Remove"), this);
381 connect(action, SIGNAL(triggered()), this, SLOT(removeEntry()));
382 actions.append(action);
384 if (actions.count() > 0)
385 QMenu::exec(actions, mapToGlobal(position));
389 \internal
391 \sa showContextMenu()
393 void QSidebar::removeEntry()
395 QList<QModelIndex> idxs = selectionModel()->selectedIndexes();
396 QList<QPersistentModelIndex> indexes;
397 for (int i = 0; i < idxs.count(); i++)
398 indexes.append(idxs.at(i));
400 for (int i = 0; i < indexes.count(); ++i)
401 model()->removeRow(indexes.at(i).row());
405 \internal
407 \sa goToUrl()
409 void QSidebar::clicked(const QModelIndex &index)
411 QUrl url = model()->index(index.row(), 0).data(QUrlModel::UrlRole).toUrl();
412 emit goTo(urlModel->qFileSystemModel()->index(url.toLocalFile()));
413 emit goToUrl(url);
414 selectUrl(url);
418 \reimp
419 Don't automatically select something
421 void QSidebar::focusInEvent(QFocusEvent *event)
423 QAbstractScrollArea::focusInEvent(event);
424 viewport()->update();
427 #endif