/* dtkFinder.cpp --- * * Author: Julien Wintz * Copyright (C) 2008 - Julien Wintz, Inria. * Created: Sat Jun 12 15:47:45 2010 (+0200) * Version: $Id: dab64846635dd0f9c802450b8e4abcaecb28859a $ * Last-Updated: lun. févr. 3 14:30:48 2014 (+0100) * By: Thibaud Kloczko * Update #: 170 */ /* Commentary: * */ /* Change log: * */ #include "dtkFinder.h" #include #ifdef Q_OS_WIN #include #endif #include // ///////////////////////////////////////////////////////////////// // dtkFinderToolBar // ///////////////////////////////////////////////////////////////// class dtkFinderToolBarPrivate { public: QToolButton *prevButton; QToolButton *nextButton; QToolButton *listViewButton; QToolButton *treeViewButton; QToolButton *showHiddenFilesButton; QLinkedList pathList; QLinkedList::iterator iterator; }; dtkFinderToolBar::dtkFinderToolBar(QWidget *parent) : QToolBar(parent), d(new dtkFinderToolBarPrivate) { d->prevButton = new QToolButton (this); d->prevButton->setArrowType (Qt::LeftArrow); d->prevButton->setEnabled (0); d->prevButton->setIconSize(QSize(16, 16)); d->prevButton->setToolTip(tr("Back")); d->nextButton = new QToolButton (this); d->nextButton->setArrowType (Qt::RightArrow); d->nextButton->setEnabled (0); d->nextButton->setIconSize(QSize(16, 16)); d->nextButton->setToolTip(tr("Next")); d->listViewButton = new QToolButton (this); d->listViewButton->setCheckable(true); d->listViewButton->setChecked (true); d->listViewButton->setIcon(QIcon(":dtkGui/pixmaps/dtk-view-list.png")); d->listViewButton->setIconSize(QSize(16, 16)); d->listViewButton->setToolTip(tr("Icon view")); d->treeViewButton = new QToolButton (this); d->treeViewButton->setCheckable(true); d->treeViewButton->setIcon(QIcon(":dtkGui/pixmaps/dtk-view-tree.png")); d->treeViewButton->setIconSize(QSize(16, 16)); d->treeViewButton->setToolTip(tr("List view")); d->showHiddenFilesButton = new QToolButton (this); d->showHiddenFilesButton->setCheckable(true); d->showHiddenFilesButton->setIcon(QIcon(":dtkGui/pixmaps/hidden-folder.png")); d->showHiddenFilesButton->setIconSize(QSize(16, 16)); d->showHiddenFilesButton->setToolTip(tr("Show/Hide hidden files")); //By default the showHiddenFilesButton is enabled in MacOS #ifdef Q_OS_MAC d->showHiddenFilesButton->setChecked(Qt::Checked); #endif QButtonGroup *viewButtonGroup = new QButtonGroup (this); viewButtonGroup->setExclusive (true); viewButtonGroup->addButton ( d->listViewButton ); viewButtonGroup->addButton ( d->treeViewButton ); this->addWidget (d->prevButton); this->addWidget (d->nextButton); this->addWidget (d->treeViewButton); this->addWidget (d->listViewButton); this->addWidget (d->showHiddenFilesButton); connect (d->prevButton, SIGNAL (clicked()), this, SLOT (onPrev())); connect (d->nextButton, SIGNAL (clicked()), this, SLOT (onNext())); connect (d->listViewButton, SIGNAL (clicked()), this, SIGNAL (listView())); connect (d->treeViewButton, SIGNAL (clicked()), this, SIGNAL (treeView())); connect (d->showHiddenFilesButton, SIGNAL(toggled(bool)), this, SIGNAL(showHiddenFiles(bool))); } dtkFinderToolBar::~dtkFinderToolBar(void) { delete d; d = NULL; } QSize dtkFinderToolBar::sizeHint (void) const { QSize size = QToolBar::sizeHint(); size.setHeight(23); return size; } void dtkFinderToolBar::setPath (const QString& path) { if (d->pathList.count()) { if (d->iterator != d->pathList.end()) d->pathList.erase(d->pathList.begin(), d->iterator); } d->pathList.prepend (path); d->iterator = d->pathList.begin(); if (d->pathList.count() > 1) d->prevButton->setEnabled(1); else d->prevButton->setEnabled(0); d->nextButton->setEnabled (0); } void dtkFinderToolBar::onNext (void) { if (d->iterator != d->pathList.begin()) { emit ( changed (*(--d->iterator)) ); d->prevButton->setEnabled (1); if (d->iterator == d->pathList.begin()) d->nextButton->setEnabled (0); } else d->nextButton->setEnabled (0); } void dtkFinderToolBar::onPrev (void) { if (d->iterator != --d->pathList.end()) { emit ( changed (*(++d->iterator)) ); d->nextButton->setEnabled(1); if (d->iterator == (--d->pathList.end())) d->prevButton->setEnabled (0); } else d->prevButton->setEnabled (0); } void dtkFinderToolBar::onTreeView(void) { d->treeViewButton->setChecked(true); d->listViewButton->setChecked(false); } void dtkFinderToolBar::onListView(void) { d->treeViewButton->setChecked(false); d->listViewButton->setChecked(true); } void dtkFinderToolBar::onShowHiddenFiles(bool show) { d->showHiddenFilesButton->setChecked(show); } // ///////////////////////////////////////////////////////////////// // dtkFinderSideView // ///////////////////////////////////////////////////////////////// class dtkFinderSideViewPrivate { public: QList items; int headerFontSize; }; dtkFinderSideView::dtkFinderSideView(QWidget *parent) : QTreeWidget(parent), d(new dtkFinderSideViewPrivate) { d->headerFontSize = 12; this->header()->hide(); this->setAcceptDrops(true); this->setDropIndicatorShown(true); this->setDragDropMode(QAbstractItemView::DropOnly); this->setIndentation(10); this->setFrameStyle(QFrame::NoFrame); this->setAttribute(Qt::WA_MacShowFocusRect, false); this->setFocusPolicy(Qt::NoFocus); this->populate(); this->setContextMenuPolicy(Qt::CustomContextMenu); connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onContextMenu(const QPoint&))); connect(this, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this, SLOT(onItemCicked(QTreeWidgetItem *, int))); } dtkFinderSideView::~dtkFinderSideView(void) { delete d; d = NULL; } void dtkFinderSideView::populate(void) { this->clear(); d->items.clear(); QFont groupFont; groupFont.setPointSize(d->headerFontSize); groupFont.setBold(true); groupFont.setCapitalization(QFont::AllUppercase); QBrush groupBrush; groupBrush.setColor(QColor("#758192")); // QFont itemFont; // itemFont.setPointSize(10); QFileIconProvider provider; QTreeWidgetItem *item1 = new QTreeWidgetItem(this, QStringList() << "Drives"); item1->setFlags(Qt::ItemIsEnabled); item1->setData(0, Qt::FontRole, groupFont); item1->setData(0, Qt::ForegroundRole, groupBrush); QFileInfoList driveList; #ifdef Q_OS_MAC QDir macDir("/Volumes"); driveList = macDir.entryInfoList(QDir::AllEntries | QDir::NoDotAndDotDot); #else driveList = QDir::drives(); #endif foreach (QFileInfo info, driveList) { QString dlabel = this->driveLabel( info.absoluteFilePath() ); QTreeWidgetItem *item = new QTreeWidgetItem(item1, QStringList() << (dlabel.isEmpty() ? "HD" : dlabel)); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); // item->setData(0, Qt::FontRole, itemFont); item->setData(0, Qt::UserRole, info.absoluteFilePath()); item->setIcon(0, provider.icon(QFileIconProvider::Drive)); d->items << item; } QTreeWidgetItem *item2 = new QTreeWidgetItem(this, QStringList() << "Places"); item2->setFlags(Qt::ItemIsEnabled); item2->setData(0, Qt::FontRole, groupFont); item2->setData(0, Qt::ForegroundRole, groupBrush); QTreeWidgetItem *item21 = new QTreeWidgetItem(item2, QStringList() << "Desktop"); item21->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); // item21->setData(0, Qt::FontRole, itemFont); item21->setData(0, Qt::UserRole, QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); item21->setIcon(0, provider.icon(QFileInfo(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)))); QTreeWidgetItem *item22 = new QTreeWidgetItem(item2, QStringList() << "Home"); item22->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); // item22->setData(0, Qt::FontRole, itemFont); item22->setData(0, Qt::UserRole, QStandardPaths::writableLocation(QStandardPaths::HomeLocation)); item22->setIcon(0, provider.icon(QFileInfo(QStandardPaths::writableLocation(QStandardPaths::HomeLocation)))); QTreeWidgetItem *item23 = new QTreeWidgetItem(item2, QStringList() << "Documents"); item23->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); // item23->setData(0, Qt::FontRole, itemFont); item23->setData(0, Qt::UserRole, QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); item23->setIcon(0, provider.icon(QFileInfo(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)))); d->items << item21 << item22 << item23; QTreeWidgetItem *item3 = new QTreeWidgetItem(this, QStringList() << "Bookmarks"); item3->setFlags(Qt::ItemIsEnabled); item3->setData(0, Qt::FontRole, groupFont); item3->setData(0, Qt::ForegroundRole, groupBrush); QSettings settings; QStringList bookmarks = settings.value("dtkFinderBookmarks").toStringList(); foreach (QString path, bookmarks) { QFileInfo info(path); if (info.exists()) { QTreeWidgetItem *item = new QTreeWidgetItem(item3, QStringList() << info.baseName()); item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); // item->setData(0, Qt::FontRole, itemFont); item->setData(0, Qt::UserRole, info.absoluteFilePath()); item->setIcon(0, provider.icon(info)); d->items << item; } } this->expandItem(item1); this->expandItem(item2); this->expandItem(item3); } void dtkFinderSideView::setHeaderFontSize(int value) { d->headerFontSize = value; this->populate(); } int dtkFinderSideView::headerFontSize(void) const { return d->headerFontSize; } QSize dtkFinderSideView::sizeHint(void) const { return QSize (-1, -1); } void dtkFinderSideView::setPath(const QString& path) { foreach (QTreeWidgetItem *item, d->items) if (item->data(0, Qt::UserRole).toString() == path) item->setSelected(true); else item->setSelected(false); } void dtkFinderSideView::addBookmark(const QString& path) { QFileInfo info(path); if (!info.isDir()) return; QSettings settings; QStringList bookmarks = settings.value("dtkFinderBookmarks").toStringList(); bookmarks.removeAll(path); bookmarks.append(path); settings.setValue("dtkFinderBookmarks", bookmarks); this->populate(); } void dtkFinderSideView::removeBookmark(const QString& path) { QFileInfo info(path); if (!info.isDir()) return; QSettings settings; QStringList bookmarks = settings.value("dtkFinderBookmarks").toStringList(); bookmarks.removeAll(path); settings.setValue("dtkFinderBookmarks", bookmarks); this->populate(); } void dtkFinderSideView::clearBookmarks(void) { QSettings settings; settings.remove("dtkFinderBookmarks"); this->populate(); } void dtkFinderSideView::onItemCicked(QTreeWidgetItem *item, int) { emit changed(item->data(0, Qt::UserRole).toString()); } void dtkFinderSideView::onContextMenu(const QPoint& position) { QTreeWidgetItem *item = this->itemAt(position); if (!item) return; QSettings settings; QStringList bookmarks = settings.value("dtkFinderBookmarks").toStringList(); QString bookmark = item->data(0, Qt::UserRole).toString(); if (!bookmarks.contains(bookmark)) return; QMenu menu; menu.addAction("Remove bookmark"); if (menu.exec(this->mapToGlobal(position))) this->removeBookmark(bookmark); } void dtkFinderSideView::dragEnterEvent(QDragEnterEvent *event) { if (event->mimeData()->hasUrls()) { QFileInfo info(event->mimeData()->urls().first().toLocalFile()); if (!info.isDir()) { event->ignore(); return; } else { event->accept(); return; } } event->ignore(); } void dtkFinderSideView::dragMoveEvent(QDragMoveEvent *event) { if (event->mimeData()->hasUrls()) { QFileInfo info(event->mimeData()->urls().first().toLocalFile()); if (!info.isDir()) { event->ignore(); return; } else { event->accept(); return; } } event->ignore(); } void dtkFinderSideView::dropEvent(QDropEvent *event) { if (event->mimeData()->hasUrls()) { QFileInfo info(event->mimeData()->urls().first().toLocalFile()); this->addBookmark(info.absoluteFilePath()); event->accept(); return; } event->ignore(); } QString dtkFinderSideView::driveLabel(QString drive) { #ifdef Q_OS_WIN drive.replace("/", "\\"); TCHAR szVolumeName[256]; TCHAR szFileSystemName[256]; DWORD dwSerialNumber = 0; DWORD dwMaxFileNameLength = 256; DWORD dwFileSystemFlags = 0; bool ret = GetVolumeInformation( drive.toLatin1().constData(), szVolumeName, 256, &dwSerialNumber, &dwMaxFileNameLength, &dwFileSystemFlags, szFileSystemName, 256); if (!ret) { drive.remove("\\"); QString decoratedDrive = "(" + drive + ")"; return decoratedDrive; } QString vName = QString::fromLatin1(szVolumeName) ; vName.trimmed(); drive.remove("\\"); vName += " (" + drive + ")"; return vName; #endif #ifdef Q_OS_MAC return QFileInfo(drive).baseName(); #endif return drive; } // ///////////////////////////////////////////////////////////////// // dtkFinderPathBarItem // ///////////////////////////////////////////////////////////////// class dtkFinderPathBarItem { public: QString text; QIcon icon; QRect rect; QDir dir; }; // ///////////////////////////////////////////////////////////////// // dtkFinderPathBar // ///////////////////////////////////////////////////////////////// class dtkFinderPathBarPrivate { public: QString path; QList items; }; dtkFinderPathBar::dtkFinderPathBar(QWidget *parent) : QFrame(parent), d(new dtkFinderPathBarPrivate) { this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); // this->setFixedHeight(23); // this->setStyleSheet("font: 11px; border-top: 1px solid #d4d4d4;"); } dtkFinderPathBar::~dtkFinderPathBar(void) { foreach (dtkFinderPathBarItem *item, d->items) delete item; delete d; d = NULL; } QSize dtkFinderPathBar::sizeHint (void) const { return QSize (-1, 23); } void dtkFinderPathBar::setPath(const QString& path) { d->path = path; foreach (dtkFinderPathBarItem *item, d->items) delete item; d->items.clear(); QFileIconProvider provider; QDir dir(d->path); do { dtkFinderPathBarItem *item = new dtkFinderPathBarItem; item->text = dir.dirName().isEmpty() ? dir.absolutePath() : dir.dirName(); if (!dir.entryInfoList(QStringList() << ".").isEmpty()) item->icon = provider.icon(dir.entryInfoList(QStringList() << ".").first()); item->dir = dir; d->items.prepend(item); } while (dir.cdUp()); this->update(); } void dtkFinderPathBar::mousePressEvent(QMouseEvent *event) { foreach (dtkFinderPathBarItem *item, d->items) { if (item->rect.contains(event->x(), event->y())) { // setPath will delete the items. Store the string so that it is still valid afterwards. QString path(item->dir.absolutePath()); this->setPath(path); emit changed(path); return; } } } void dtkFinderPathBar::paintEvent(QPaintEvent *event) { DTK_UNUSED(event); QRect rect = this->rect(); int x = 10; int y = (rect.height() - 16) / 2; QPainter painter(this); foreach (dtkFinderPathBarItem *item, d->items) { painter.drawPixmap(x, y, item->icon.pixmap(16, 16)); painter.drawText(x + 16 + 4, y + 14, item->text); int delta = 16 + 4 + painter.fontMetrics().width(item->text) + 10; item->rect = QRect(x, 0, delta, rect.height()); x += delta; } if (d->items.last()->rect.right() >= this->width()) { QRect prect(this->width() - 30, 0, 30, rect.height()); painter.fillRect(prect, this->palette().background()); painter.drawText(prect.left(), y + 14, "..."); } } // ///////////////////////////////////////////////////////////////// // dtkFinderListView // ///////////////////////////////////////////////////////////////// class dtkFinderListViewPrivate { public: QMenu *menu; QList defaultActions; QList customActions; QAction *bookmarkAction; bool allowFileBookmarking; }; dtkFinderListView::dtkFinderListView(QWidget *parent) : QListView(parent), d(new dtkFinderListViewPrivate) { this->setViewMode(QListView::IconMode); this->setWordWrap(true); this->setWrapping(true); this->setIconSize(QSize(64, 64)); this->setResizeMode(QListView::Adjust); this->setGridSize(QSize(128, 96)); this->setFrameStyle(QFrame::NoFrame); this->setAttribute(Qt::WA_MacShowFocusRect, false); this->setContextMenuPolicy(Qt::CustomContextMenu); d->menu = new QMenu(this); d->allowFileBookmarking = true; d->bookmarkAction = new QAction(tr("Bookmark"), this); d->bookmarkAction->setIconVisibleInMenu(true); d->bookmarkAction->setIcon(QIcon(":dtkGui/pixmaps/star.svg")); connect(d->bookmarkAction, SIGNAL(triggered()), this, SLOT(onBookmarkSelectedItemsRequested())); d->menu->addAction(d->bookmarkAction); connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(updateContextMenu(const QPoint&))); } dtkFinderListView::~dtkFinderListView(void) { delete d; d = NULL; } void dtkFinderListView::addContextMenuAction(QAction *action) { d->customActions.append(action); } void dtkFinderListView::addDefaultContextMenuAction(QAction *action) { d->defaultActions.append(action); } /** Returns the currently selected path, or the first one if more than one item is selected. */ QString dtkFinderListView::selectedPath() const { if (!selectedIndexes().count()) return QString(); return this->selectedPaths()[0]; } /** Returns the currently selected paths. */ QStringList dtkFinderListView::selectedPaths() const { if (!selectedIndexes().count()) return QStringList(); if (QFileSystemModel *model = qobject_cast(this->model())) { QStringList selectedPaths = *(new QStringList()); foreach (QModelIndex index, selectedIndexes()) selectedPaths << model->filePath(index); return selectedPaths; } else return QStringList(); } /** * Set the allowance of file bookmarking. * @param isAllowed - whether is allowed to bookmark files **/ void dtkFinderListView::allowFileBookmarking(bool isAllowed) { d->allowFileBookmarking = isAllowed; } void dtkFinderListView::updateContextMenu(const QPoint& point) { QModelIndex index = this->indexAt(point); d->menu->clear(); if (index.isValid()) { if (!d->allowFileBookmarking) { bool removed = false; QString selectedPath = this->selectedPath(); if (!selectedPath.isEmpty()) { QFileInfo fileInfo = QFileInfo(selectedPath); if (fileInfo.isFile()) { d->menu->removeAction(d->bookmarkAction); removed = true; } } if (!removed) { if (d->menu->actions().size() > 0) d->menu->insertAction(d->menu->actions()[0], d->bookmarkAction); else d->menu->addAction(d->bookmarkAction); } } //Add custom actions for (int i = 0; i < d->customActions.size(); i++) { d->menu->addAction(d->customActions.value(i)); } d->menu->addSeparator(); } //By default add default action for (int i = 0; i < d->defaultActions.size(); i++) { d->menu->addAction(d->defaultActions.value(i)); } d->menu->exec(mapToGlobal(point)); } void dtkFinderListView::onBookmarkSelectedItemsRequested(void) { if (!selectedIndexes().count()) return; foreach (QString path, this->selectedPaths()) emit bookmarked(path); } void dtkFinderListView::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Up && (event->modifiers() & Qt::ControlModifier)) { if (QFileSystemModel *model = qobject_cast(this->model())) { QDir dir = QDir(model->filePath(this->rootIndex())); dir.cdUp(); this->setRootIndex(model->index(dir.absolutePath())); emit changed(dir.absolutePath()); event->accept(); return; } } if (event->key() == Qt::Key_Down && (event->modifiers() & Qt::ControlModifier)) { if (QFileSystemModel *model = qobject_cast(this->model())) { if (!this->selectionModel()->selectedIndexes().count()) return; QFileInfo selection = model->fileInfo(this->selectionModel()->selectedIndexes().first()); if (selection.isDir()) { this->setRootIndex(model->index(selection.filePath())); emit changed(selection.absoluteFilePath()); event->accept(); } return; } } QListView::keyPressEvent(event); } void dtkFinderListView::mouseDoubleClickEvent(QMouseEvent *event) { if (QFileSystemModel *model = qobject_cast(this->model())) { Q_UNUSED(model); QModelIndex index = indexAt(event->pos()); if (!index.isValid()) return; QListView::mouseDoubleClickEvent(event); } } void dtkFinderListView::startDrag(Qt::DropActions supportedActions) { QModelIndexList indexes = selectedIndexes(); if (indexes.count() > 0) { QMimeData *data = model()->mimeData(indexes); if (!data) return; QFileIconProvider provider; QPixmap pixmap = provider.icon(QFileInfo(data->urls().first().toLocalFile())).pixmap(64, 64); QDrag *drag = new QDrag(this); drag->setPixmap(pixmap); drag->setMimeData(data); drag->setHotSpot(QPoint(pixmap.width() / 2, pixmap.height() / 2)); drag->exec(supportedActions, Qt::IgnoreAction); } } // ///////////////////////////////////////////////////////////////// // dtkFinderTreeView // ///////////////////////////////////////////////////////////////// class dtkFinderTreeViewPrivate { public: QMenu *menu; QList defaultActions; QList customActions; QAction *bookmarkAction; bool allowFileBookmarking; }; dtkFinderTreeView::dtkFinderTreeView(QWidget *parent) : QTreeView(parent), d(new dtkFinderTreeViewPrivate) { this->setDragEnabled(true); this->setFrameStyle(QFrame::NoFrame); this->setAttribute(Qt::WA_MacShowFocusRect, false); this->setContextMenuPolicy(Qt::CustomContextMenu); this->setSortingEnabled(true); this->sortByColumn(0, Qt::AscendingOrder); d->menu = new QMenu(this); d->allowFileBookmarking = true; d->bookmarkAction = new QAction(tr("Bookmark"), this); d->bookmarkAction->setIconVisibleInMenu(true); d->bookmarkAction->setIcon(QIcon(":dtkGui/pixmaps/star.svg")); connect(d->bookmarkAction, SIGNAL(triggered()), this, SLOT(onBookmarkSelectedItemsRequested())); d->menu->addAction(d->bookmarkAction); connect(this, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(updateContextMenu(const QPoint&))); } dtkFinderTreeView::~dtkFinderTreeView(void) { delete d; d = NULL; } int dtkFinderTreeView::sizeHintForColumn(int column) const { if (column == 1) return 400; return 100; } void dtkFinderTreeView::addContextMenuAction(QAction *action) { d->customActions.append(action); } void dtkFinderTreeView::addDefaultContextMenuAction(QAction *action) { d->defaultActions.append(action); } /** Returns the currently selected path, or the first one if more than one item is selected. */ QString dtkFinderTreeView::selectedPath() const { if (!selectedIndexes().count()) return QString(); return this->selectedPaths()[0]; } /** Returns the currently selected paths. */ QStringList dtkFinderTreeView::selectedPaths() const { if (!selectedIndexes().count()) return QStringList(); // the treeview considers each cell as a selected item // hence we will need to group items by row // or take one item per row QList alreadyReadRows; if (QFileSystemModel *model = qobject_cast(this->model())) { QStringList selectedPaths = *(new QStringList()); foreach (QModelIndex index, selectedIndexes()) { if (!alreadyReadRows.contains(index.row())) { selectedPaths << model->filePath(index); alreadyReadRows << index.row(); } } return selectedPaths; } else return QStringList(); } /** * Set the allowance of file bookmarking. * @param isAllowed - whether is allowed to bookmark files **/ void dtkFinderTreeView::allowFileBookmarking(bool isAllowed) { d->allowFileBookmarking = isAllowed; } void dtkFinderTreeView::updateContextMenu(const QPoint& point) { QModelIndex index = this->indexAt(point); d->menu->clear(); if (index.isValid()) { if (!d->allowFileBookmarking) { bool removed = false; QString selectedPath = this->selectedPath(); if (!selectedPath.isEmpty()) { QFileInfo fileInfo = QFileInfo(selectedPath); if (fileInfo.isFile()) { d->menu->removeAction(d->bookmarkAction); removed = true; } } if (!removed) { if (d->menu->actions().size() > 0) d->menu->insertAction(d->menu->actions()[0], d->bookmarkAction); else d->menu->addAction(d->bookmarkAction); } } //Add custom actions for (int i = 0; i < d->customActions.size(); i++) { d->menu->addAction(d->customActions.value(i)); } d->menu->addSeparator(); } //By default add default action for (int i = 0; i < d->defaultActions.size(); i++) { d->menu->addAction(d->defaultActions.value(i)); } d->menu->exec(mapToGlobal(point)); } void dtkFinderTreeView::onBookmarkSelectedItemsRequested(void) { if (!selectedIndexes().count()) return; foreach (QString path, this->selectedPaths()) emit bookmarked(path); } void dtkFinderTreeView::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Up && (event->modifiers() & Qt::ControlModifier)) { if (QFileSystemModel *model = qobject_cast(this->model())) { QDir dir = QDir(model->filePath(this->rootIndex())); dir.cdUp(); this->setRootIndex(model->index(dir.absolutePath())); emit changed(dir.absolutePath()); event->accept(); return; } } if (event->key() == Qt::Key_Down && (event->modifiers() & Qt::ControlModifier)) { if (QFileSystemModel *model = qobject_cast(this->model())) { if (!this->selectionModel()->selectedIndexes().count()) return; QFileInfo selection = model->fileInfo(this->selectionModel()->selectedIndexes().first()); if (selection.isDir()) { this->setRootIndex(model->index(selection.filePath())); emit changed(selection.absoluteFilePath()); event->accept(); } return; } } QTreeView::keyPressEvent(event); } void dtkFinderTreeView::mouseDoubleClickEvent(QMouseEvent *event) { if (QFileSystemModel *model = qobject_cast(this->model())) { Q_UNUSED(model); QModelIndex index = indexAt(event->pos()); if (!index.isValid()) return; QTreeView::mouseDoubleClickEvent(event); } } void dtkFinderTreeView::startDrag(Qt::DropActions supportedActions) { QModelIndexList indexes = selectedIndexes(); if (indexes.count() > 0) { QMimeData *data = model()->mimeData(indexes); if (!data) return; QFileIconProvider provider; QPixmap pixmap = provider.icon(QFileInfo(data->urls().first().toLocalFile())).pixmap(64, 64); QDrag *drag = new QDrag(this); drag->setPixmap(pixmap); drag->setMimeData(data); drag->setHotSpot(QPoint(pixmap.width() / 2, pixmap.height() / 2)); drag->exec(supportedActions, Qt::IgnoreAction); } } void dtkFinderTreeView::resizeEvent(QResizeEvent *event) { DTK_UNUSED(event); this->setColumnWidth(0, this->width() / 2); } // ///////////////////////////////////////////////////////////////// // dtkFinder // ///////////////////////////////////////////////////////////////// class dtkFinderPrivate { public: QFileSystemModel *model; bool isAllowedMultipleSelection; bool hiddenFilesShown; QAction *iconviewAction; QAction *listviewAction; QAction *showHideAction; dtkFinderListView *list; dtkFinderTreeView *tree; QStackedWidget *stack; }; dtkFinder::dtkFinder(QWidget *parent) : QWidget(parent), d(new dtkFinderPrivate) { d->model = new QFileSystemModel(this); d->model->setFilter(QDir::AllEntries | QDir::NoDotAndDotDot); d->hiddenFilesShown = false; //By default the showHiddenFilesButton is enabled in MacOS #ifdef Q_OS_MAC d->hiddenFilesShown = true; d->model->setFilter(QDir::AllEntries | QDir::Hidden | QDir::NoDotAndDotDot); #endif d->model->setRootPath(QDir::rootPath()); d->list = new dtkFinderListView(this); d->list->setModel(d->model); d->list->setRootIndex(d->model->index(QDir::currentPath())); d->tree = new dtkFinderTreeView(this); d->tree->setModel(d->model); d->tree->setRootIndex(d->model->index(QDir::currentPath())); d->tree->setSelectionBehavior(QAbstractItemView::SelectRows); d->tree->setAllColumnsShowFocus(true); d->iconviewAction = new QAction(tr("Icon view"), this); d->iconviewAction->setIconVisibleInMenu(true); d->iconviewAction->setIcon(QIcon(":dtkGui/pixmaps/dtk-view-list.png")); d->listviewAction = new QAction(tr("List view"), this); d->listviewAction->setIconVisibleInMenu(true); d->listviewAction->setIcon(QIcon(":dtkGui/pixmaps/dtk-view-tree.png")); d->showHideAction = new QAction(tr("Show hidden files"), this); //By default the showHiddenFilesButton is enabled in MacOS #ifdef Q_OS_MAC d->showHideAction->setText(tr("Hide hidden files")); #endif d->showHideAction->setIconVisibleInMenu(true); d->showHideAction->setIcon(QIcon(":dtkGui/pixmaps/hidden-folder.png")); d->list->addDefaultContextMenuAction(d->listviewAction); d->list->addDefaultContextMenuAction(d->showHideAction); d->tree->addDefaultContextMenuAction(d->iconviewAction); d->tree->addDefaultContextMenuAction(d->showHideAction); d->stack = new QStackedWidget(this); d->stack->addWidget(d->list); d->stack->addWidget(d->tree); d->stack->setCurrentIndex(0); QVBoxLayout *layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(0); layout->addWidget(d->stack); connect(d->iconviewAction, SIGNAL(triggered()), this, SLOT(switchToListView())); connect(d->listviewAction, SIGNAL(triggered()), this, SLOT(switchToTreeView())); connect(d->showHideAction, SIGNAL(triggered()), this, SLOT(switchShowHiddenFiles())); connect(d->list, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(onIndexDoubleClicked(QModelIndex))); connect(d->tree, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(onIndexDoubleClicked(QModelIndex))); connect(d->list, SIGNAL(clicked(QModelIndex)), this, SLOT(onIndexClicked(QModelIndex))); connect(d->tree, SIGNAL(clicked(QModelIndex)), this, SLOT(onIndexClicked(QModelIndex))); connect(d->list, SIGNAL(changed(QString)), this, SLOT(setPath(QString))); connect(d->tree, SIGNAL(changed(QString)), this, SLOT(setPath(QString))); connect(d->list, SIGNAL(changed(QString)), this, SIGNAL(changed(QString))); connect(d->tree, SIGNAL(changed(QString)), this, SIGNAL(changed(QString))); connect(d->list, SIGNAL(bookmarked(QString)), this, SIGNAL(bookmarked(QString))); connect(d->tree, SIGNAL(bookmarked(QString)), this, SIGNAL(bookmarked(QString))); connect(d->list->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), this, SLOT(onSelectionChanged(const QItemSelection&, const QItemSelection&))); connect(d->tree->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), this, SLOT(onSelectionChanged(const QItemSelection&, const QItemSelection&))); QAction *switchToListViewAction = new QAction(this); QAction *switchToTreeViewAction = new QAction(this); switchToListViewAction->setShortcut(Qt::ControlModifier + Qt::Key_1); switchToTreeViewAction->setShortcut(Qt::ControlModifier + Qt::Key_2); this->addAction(switchToListViewAction); this->addAction(switchToTreeViewAction); connect(switchToListViewAction, SIGNAL(triggered()), this, SLOT(switchToListView())); connect(switchToTreeViewAction, SIGNAL(triggered()), this, SLOT(switchToTreeView())); } dtkFinder::~dtkFinder(void) { delete d; d = NULL; } void dtkFinder::addContextMenuAction(QAction *action) { d->list->addContextMenuAction(action); d->tree->addContextMenuAction(action); } /** Returns the currently selected path, or the first one if more than one item is selected. */ QString dtkFinder::selectedPath(void) const { if (d->stack->currentIndex() == 0) return d->list->selectedPath(); if (d->stack->currentIndex() == 1) return d->tree->selectedPath(); return QString(); } /** Returns the currently selected paths. */ QStringList dtkFinder::selectedPaths() const { if (d->stack->currentIndex() == 0) return d->list->selectedPaths(); if (d->stack->currentIndex() == 1) return d->tree->selectedPaths(); return QStringList(); } /** * Set the allowance of file bookmarking. * @param isAllowed - whether is allowed to bookmark files **/ void dtkFinder::allowFileBookmarking(bool isAllowed) { d->list->allowFileBookmarking(isAllowed); d->tree->allowFileBookmarking(isAllowed); } /** Set whether multiple files can be selected at the same time. */ void dtkFinder::allowMultipleSelection(bool isAllowed) { d->isAllowedMultipleSelection = isAllowed; if (isAllowed) { d->list->setSelectionMode(QAbstractItemView::ExtendedSelection); d->tree->setSelectionMode(QAbstractItemView::ExtendedSelection); } else { d->list->setSelectionMode(QAbstractItemView::SingleSelection); d->tree->setSelectionMode(QAbstractItemView::SingleSelection); } } void dtkFinder::setPath(const QString& path) { if (sender() != d->list) d->list->setRootIndex(d->model->index(path)); if (sender() != d->tree) d->tree->setRootIndex(d->model->index(path)); } void dtkFinder::switchToListView(void) { emit listView(); d->stack->setCurrentIndex(0); emitSelectedItems(); } void dtkFinder::switchToTreeView(void) { emit treeView(); d->stack->setCurrentIndex(1); emitSelectedItems(); } void dtkFinder::onShowHiddenFiles(bool show) { if (show) { d->hiddenFilesShown = true; d->showHideAction->setText(tr("Hide hidden files")); d->model->setFilter(QDir::Hidden | QDir::AllEntries | QDir::NoDotAndDotDot); } else { d->hiddenFilesShown = false; d->showHideAction->setText(tr("Show hidden files")); d->model->setFilter(QDir::AllEntries | QDir::NoDotAndDotDot); } } void dtkFinder::switchShowHiddenFiles() { // this->onShowHiddenFiles(!d->hiddenFilesShown); emit showHiddenFiles(!d->hiddenFilesShown); } void dtkFinder::onIndexClicked(QModelIndex index) { emit fileClicked(d->model->fileInfo(index)); } void dtkFinder::onIndexDoubleClicked(QModelIndex index) { QFileInfo selection = d->model->fileInfo(index); if (selection.isDir()) { QModelIndex idx = d->model->index(d->model->filePath(index)); d->list->setRootIndex(idx); d->tree->setRootIndex(idx); emit changed(selection.absoluteFilePath()); } else emit fileDoubleClicked(selection.absoluteFilePath()); emit nothingSelected(); } /** Bookmarks the currently selected item(s). */ void dtkFinder::onBookmarkSelectedItemsRequested(void) { if (d->stack->currentIndex() == 0) d->list->onBookmarkSelectedItemsRequested(); if (d->stack->currentIndex() == 1) d->tree->onBookmarkSelectedItemsRequested(); } void dtkFinder::emitSelectedItems() { QStringList selectedPaths = *(new QStringList()); if (d->stack->currentIndex() == 0) selectedPaths = d->list->selectedPaths(); if (d->stack->currentIndex() == 1) selectedPaths = d->tree->selectedPaths(); emit selectionChanged(selectedPaths); if (!selectedPaths.size()) emit nothingSelected(); } void dtkFinder::onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) { // note that only the recently selected items are in the "selected" variable // items previously selected are not emitSelectedItems(); }