1 /****************************************************************************
3 ** Copyright (C) 1992-2007 Trolltech ASA. All rights reserved.
5 ** This file is part of the QtGui module of the Qt Toolkit.
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 ****************************************************************************/
25 #include "qfilesystemmodel_p.h"
27 #ifndef QT_NO_FILEDIALOG
32 #include <qmimedata.h>
35 #if QT_VERSION >= 0x040300
36 #include <qfileiconprovider.h>
38 #include <qdirmodel.h>
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)
54 QStringList
QUrlModel::mimeTypes() const
56 return QStringList(QLatin1String("text/uri-list"));
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
;
83 QMimeData
*QUrlModel::mimeData(const QModelIndexList
&indexes
) const
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();
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()))
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
))
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()))
125 addUrls(data
->urls(), row
);
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
),
142 QStandardItemModel::setData(index
, url
, UrlRole
);
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
);
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());
188 Add urls \a list into the list at \a row. If move then movie
189 existing ones to row.
193 void QUrlModel::addUrls(const QList
<QUrl
> &list
, int row
, bool move
)
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"))
202 for (int j
= 0; move
&& j
< rowCount(); ++j
) {
203 if (index(j
, 0).data(UrlRole
) == url
) {
211 QModelIndex idx
= fileSystemModel
->index(url
.toLocalFile());
212 if (!fileSystemModel
->isDir(idx
))
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
226 for (int i
= 0; i
< rowCount(); ++i
)
227 list
.append(data(index(i
, 0), UrlRole
).toUrl());
232 QFileSystemModel to get index's from, clears existing rows
234 void QUrlModel::setFileSystemModel(QFileSystemModel
*model
)
236 if (model
== fileSystemModel
)
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()));
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()
282 for (int i
= 0; i
< watching
.count(); ++i
)
283 paths
.append(watching
.at(i
).second
);
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())
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
);
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
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
);
367 connect(selectionModel(), SIGNAL(currentChanged(const QModelIndex
&, const QModelIndex
&)),
368 this, SLOT(clicked(const QModelIndex
&)));
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
));
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());
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()));
419 Don't automatically select something
421 void QSidebar::focusInEvent(QFocusEvent
*event
)
423 QAbstractScrollArea::focusInEvent(event
);
424 viewport()->update();