diff --git a/libfm-qt/CMakeLists.txt b/libfm-qt/CMakeLists.txt index 84c8049..c0cbc1e 100644 --- a/libfm-qt/CMakeLists.txt +++ b/libfm-qt/CMakeLists.txt @@ -63,6 +63,8 @@ set(libfm_SRCS appchoosercombobox.cpp appmenuview.cpp appchooserdialog.cpp + filesearchdialog.cpp + fm-search.c # might be moved to libfm later ) set(libfm_UIS @@ -73,6 +75,7 @@ set(libfm_UIS edit-bookmarks.ui exec-file.ui app-chooser-dialog.ui + filesearch.ui ) qt5_wrap_ui(libfm_UIS_H ${libfm_UIS}) diff --git a/libfm-qt/filemenu.cpp b/libfm-qt/filemenu.cpp index 9dbe7e0..15dcb38 100644 --- a/libfm-qt/filemenu.cpp +++ b/libfm-qt/filemenu.cpp @@ -62,6 +62,19 @@ void FileMenu::createMenu(FmFileInfoList* files, FmFileInfo* info, FmPath* cwd) confirmDelete_ = true; confirmTrash_ = false; // Confirm before moving files into "trash can" + openAction_ = NULL; + openWithMenuAction_ = NULL; + openWithAction_ = NULL; + separator1_ = NULL; + cutAction_ = NULL; + copyAction_ = NULL; + pasteAction_ = NULL; + deleteAction_ = NULL; + unTrashAction_ = NULL; + renameAction_ = NULL; + separator2_ = NULL; + propertiesAction_ = NULL; + files_ = fm_file_info_list_ref(files); info_ = info ? fm_file_info_ref(info) : NULL; cwd_ = cwd ? fm_path_ref(cwd) : NULL; @@ -346,8 +359,10 @@ void FileMenu::onRenameTriggered() { void FileMenu::setUseTrash(bool trash) { if(useTrash_ != trash) { useTrash_ = trash; - deleteAction_->setText(useTrash_ ? tr("&Move to Trash") : tr("&Delete")); - deleteAction_->setIcon(useTrash_ ? QIcon::fromTheme("user-trash") : QIcon::fromTheme("edit-delete")); + if(deleteAction_) { + deleteAction_->setText(useTrash_ ? tr("&Move to Trash") : tr("&Delete")); + deleteAction_->setIcon(useTrash_ ? QIcon::fromTheme("user-trash") : QIcon::fromTheme("edit-delete")); + } } } diff --git a/libfm-qt/filesearch.ui b/libfm-qt/filesearch.ui new file mode 100644 index 0000000..f5f6051 --- /dev/null +++ b/libfm-qt/filesearch.ui @@ -0,0 +1,449 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>SearchDialog</class> + <widget class="QDialog" name="SearchDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>512</width> + <height>420</height> + </rect> + </property> + <property name="windowTitle"> + <string>Search Files</string> + </property> + <property name="windowIcon"> + <iconset theme="system-search"> + <normaloff/> + </iconset> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QTabWidget" name="tabWidget"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="tab"> + <attribute name="title"> + <string>Name/Location</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,1"> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>File Name Patterns:</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QLineEdit" name="namePatterns"> + <property name="text"> + <string>*</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="nameCaseInsensitive"> + <property name="text"> + <string>Case insensitive</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="nameRegExp"> + <property name="text"> + <string>Use regular expression</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>Places to Search:</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_5"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QListWidget" name="listView"/> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout_4"> + <item> + <widget class="QPushButton" name="addPath"> + <property name="text"> + <string>&Add</string> + </property> + <property name="icon"> + <iconset theme="list-add"> + <normaloff/> + </iconset> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="removePath"> + <property name="text"> + <string>&Remove</string> + </property> + <property name="icon"> + <iconset theme="list-remove"> + <normaloff/> + </iconset> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </item> + <item> + <widget class="QCheckBox" name="recursiveSearch"> + <property name="text"> + <string>Search in sub directories</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="searchHidden"> + <property name="text"> + <string>Search for hidden files</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_2"> + <attribute name="title"> + <string>File Type</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_7"> + <item> + <widget class="QGroupBox" name="groupBox_3"> + <property name="title"> + <string>Only search for files of following types:</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_6"> + <item> + <widget class="QCheckBox" name="searchTextFiles"> + <property name="text"> + <string>Text files</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="searchImages"> + <property name="text"> + <string>Image files</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="searchAudio"> + <property name="text"> + <string>Audio files</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="searchVideo"> + <property name="text"> + <string>Video files</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="searchDocuments"> + <property name="text"> + <string>Documents</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="searchFolders"> + <property name="text"> + <string>Folders</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_3"> + <attribute name="title"> + <string>Content</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_9"> + <item> + <widget class="QGroupBox" name="groupBox_4"> + <property name="title"> + <string>File contains:</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_8"> + <item> + <widget class="QLineEdit" name="contentPattern"/> + </item> + <item> + <widget class="QCheckBox" name="contentCaseInsensitive"> + <property name="text"> + <string>Case insensiti&ve</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="contentRegExp"> + <property name="text"> + <string>&Use regular expression</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer_3"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>186</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <widget class="QWidget" name="tab_4"> + <attribute name="title"> + <string>Properties</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_10"> + <item> + <widget class="QGroupBox" name="groupBox_5"> + <property name="title"> + <string>File Size:</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QCheckBox" name="largerThan"> + <property name="text"> + <string>Larger than:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QSpinBox" name="minSize"/> + </item> + <item> + <widget class="QComboBox" name="minSizeUnit"> + <property name="currentIndex"> + <number>2</number> + </property> + <item> + <property name="text"> + <string>Bytes</string> + </property> + </item> + <item> + <property name="text"> + <string>KiB</string> + </property> + </item> + <item> + <property name="text"> + <string>MiB</string> + </property> + </item> + <item> + <property name="text"> + <string>GiB</string> + </property> + </item> + </widget> + </item> + </layout> + </item> + <item row="1" column="0"> + <widget class="QCheckBox" name="smallerThan"> + <property name="text"> + <string>Smaller than:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QSpinBox" name="maxSize"/> + </item> + <item> + <widget class="QComboBox" name="maxSizeUnit"> + <property name="currentIndex"> + <number>2</number> + </property> + <item> + <property name="text"> + <string>Bytes</string> + </property> + </item> + <item> + <property name="text"> + <string>KiB</string> + </property> + </item> + <item> + <property name="text"> + <string>MiB</string> + </property> + </item> + <item> + <property name="text"> + <string>GiB</string> + </property> + </item> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_6"> + <property name="title"> + <string>Last Modified Time:</string> + </property> + <layout class="QFormLayout" name="formLayout_2"> + <item row="0" column="0"> + <widget class="QCheckBox" name="earlierThan"> + <property name="text"> + <string>Earlier than:</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QCheckBox" name="laterThan"> + <property name="text"> + <string>Later than:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QDateEdit" name="maxTime"> + <property name="calendarPopup"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QDateEdit" name="minTime"> + <property name="calendarPopup"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="verticalSpacer_4"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>SearchDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>SearchDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/libfm-qt/filesearchdialog.cpp b/libfm-qt/filesearchdialog.cpp new file mode 100644 index 0000000..eeaeaa3 --- /dev/null +++ b/libfm-qt/filesearchdialog.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2015 Hong Jen Yee (PCMan) <pcman.tw@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "filesearchdialog.h" +#include <QMessageBox> +#include "fm-search.h" +#include "ui_filesearch.h" +#include <limits> +#include <QFileDialog> + +namespace Fm { + +FileSearchDialog::FileSearchDialog(QStringList paths, QWidget* parent, Qt::WindowFlags f): + QDialog(parent, f), + ui(new Ui::SearchDialog()) { + ui->setupUi(this); + ui->minSize->setMaximum(std::numeric_limits<int>().max()); + ui->maxSize->setMaximum(std::numeric_limits<int>().max()); + Q_FOREACH(const QString& path, paths) { + ui->listView->addItem(path); + } + + ui->maxTime->setDate(QDate::currentDate()); + ui->minTime->setDate(QDate::currentDate()); + + connect(ui->addPath, &QPushButton::clicked, this, &FileSearchDialog::onAddPath); + connect(ui->removePath, &QPushButton::clicked, this, &FileSearchDialog::onRemovePath); +} + +FileSearchDialog::~FileSearchDialog() { + delete ui; +} + +void FileSearchDialog::accept() { + // build the search:/// uri + int n = ui->listView->count(); + if(n > 0) { + FmSearch* search = fm_search_new(); + int i; + for(i = 0; i < n; ++i) { // add directories + QListWidgetItem* item = ui->listView->item(i); + fm_search_add_dir(search, item->text().toLocal8Bit().constData()); + } + + fm_search_set_recursive(search, ui->recursiveSearch->isChecked()); + fm_search_set_show_hidden(search, ui->searchHidden->isChecked()); + fm_search_set_name_patterns(search, ui->namePatterns->text().toUtf8().constData()); + fm_search_set_name_ci(search, ui->nameCaseInsensitive->isChecked()); + fm_search_set_name_regex(search, ui->nameRegExp->isChecked()); + + fm_search_set_content_pattern(search, ui->contentPattern->text().toUtf8().constData()); + fm_search_set_content_ci(search, ui->contentCaseInsensitive->isChecked()); + fm_search_set_content_regex(search, ui->contentRegExp->isChecked()); + + // search for the files of specific mime-types + if(ui->searchTextFiles->isChecked()) + fm_search_add_mime_type(search, "text/plain"); + if(ui->searchImages->isChecked()) + fm_search_add_mime_type(search, "image/*"); + if(ui->searchAudio->isChecked()) + fm_search_add_mime_type(search, "audio/*"); + if(ui->searchVideo->isChecked()) + fm_search_add_mime_type(search, "video/*"); + if(ui->searchFolders->isChecked()) + fm_search_add_mime_type(search, "inode/directory"); + if(ui->searchDocuments->isChecked()) { + const char* doc_types[] = { + "application/pdf", + /* "text/html;" */ + "application/vnd.oasis.opendocument.*", + "application/vnd.openxmlformats-officedocument.*", + "application/msword;application/vnd.ms-word", + "application/msexcel;application/vnd.ms-excel" + }; + for(i = 0; i < sizeof(doc_types)/sizeof(char*); ++i) + fm_search_add_mime_type(search, doc_types[i]); + } + + // search based on file size + const unsigned int unit_bytes[] = {1, (1024), (1024*1024), (1024*1024*1024)}; + if(ui->largerThan->isChecked()) { + guint64 size = ui->minSize->value() * unit_bytes[ui->minSizeUnit->currentIndex()]; + fm_search_set_min_size(search, size); + } + + if(ui->smallerThan->isChecked()) { + guint64 size = ui->maxSize->value() * unit_bytes[ui->maxSizeUnit->currentIndex()]; + fm_search_set_min_size(search, size); + } + + // search based on file mtime (we only support date in YYYY-MM-DD format) + if(ui->earlierThan->isChecked()) { + fm_search_set_max_mtime(search, ui->maxTime->date().toString(QStringLiteral("yyyy-MM-dd")).toUtf8().constData()); + } + if(ui->laterThan->isChecked()) { + fm_search_set_min_mtime(search, ui->minTime->date().toString(QStringLiteral("yyyy-MM-dd")).toUtf8().constData()); + } + + searchUri_.take(fm_search_dup_path(search)); + + fm_search_free(search); + } + else { + QMessageBox::critical(this, tr("Error"), tr("You should add at least add one directory to search.")); + return; + } + QDialog::accept(); +} + +void FileSearchDialog::onAddPath() { + QString dir = QFileDialog::getExistingDirectory(this, tr("Select a folder")); + if(dir.isEmpty()) + return; + // avoid adding duplicated items + if(ui->listView->findItems(dir, Qt::MatchFixedString|Qt::MatchCaseSensitive).isEmpty()) { + ui->listView->addItem(dir); + } +} + +void FileSearchDialog::onRemovePath() { + // remove selected items + Q_FOREACH(QListWidgetItem* item, ui->listView->selectedItems()) { + delete item; + } +} + +} diff --git a/libfm-qt/filesearchdialog.h b/libfm-qt/filesearchdialog.h new file mode 100644 index 0000000..dc6dc08 --- /dev/null +++ b/libfm-qt/filesearchdialog.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 Hong Jen Yee (PCMan) <pcman.tw@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef FM_FILESEARCHDIALOG_H +#define FM_FILESEARCHDIALOG_H + +#include "libfmqtglobals.h" +#include <QDialog> +#include "path.h" + +namespace Ui { + class SearchDialog; +} + +namespace Fm { + +class LIBFM_QT_API FileSearchDialog : public QDialog +{ +public: + FileSearchDialog(QStringList paths = QStringList(), QWidget * parent = 0, Qt::WindowFlags f = 0); + ~FileSearchDialog(); + + Path searchUri() const { + return searchUri_; + } + + virtual void accept(); + +private Q_SLOTS: + void onAddPath(); + void onRemovePath(); + +private: + Ui::SearchDialog* ui; + Path searchUri_; +}; + +} + +#endif // FM_FILESEARCHDIALOG_H diff --git a/libfm-qt/fm-search.c b/libfm-qt/fm-search.c new file mode 100644 index 0000000..b8e26ca --- /dev/null +++ b/libfm-qt/fm-search.c @@ -0,0 +1,317 @@ +/* + * fm-search-uri.c + * + * Copyright 2015 Hong Jen Yee (PCMan) <pcman.tw@gmail.com> + * Copyright 2012-2014 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include "fm-search.h" +#include <string.h> + +struct _FmSearch +{ + gboolean recursive; + gboolean show_hidden; + char* name_patterns; + gboolean name_ci; + gboolean name_regex; + char* content_pattern; + gboolean content_ci; + gboolean content_regex; + GList* mime_types; + GList* search_path_list; + guint64 max_size; + guint64 min_size; + char* max_mtime; + char* min_mtime; +}; + +FmSearch* fm_search_new (void) +{ + FmSearch* search = (FmSearch*)g_slice_new0(FmSearch); + return search; +} + +void fm_search_free(FmSearch* search) +{ + g_list_free_full(search->mime_types, (GDestroyNotify)g_free); + g_list_free_full(search->search_path_list, (GDestroyNotify)g_free); + g_free(search->name_patterns); + g_free(search->content_pattern); + g_free(search->max_mtime); + g_free(search->min_mtime); + g_slice_free(FmSearch, search); +} + +gboolean fm_search_get_recursive(FmSearch* search) +{ + return search->recursive; +} + +void fm_search_set_recursive(FmSearch* search, gboolean recursive) +{ + search->recursive = recursive; +} + +gboolean fm_search_get_show_hidden(FmSearch* search) +{ + return search->show_hidden; +} + +void fm_search_set_show_hidden(FmSearch* search, gboolean show_hidden) +{ + search->show_hidden = show_hidden; +} + +const char* fm_search_get_name_patterns(FmSearch* search) +{ + return search->name_patterns; +} + +void fm_search_set_name_patterns(FmSearch* search, const char* name_patterns) +{ + g_free(search->name_patterns); + search->name_patterns = g_strdup(name_patterns); +} + +gboolean fm_search_get_name_ci(FmSearch* search) +{ + return search->name_ci; +} + +void fm_search_set_name_ci(FmSearch* search, gboolean name_ci) +{ + search->name_ci = name_ci; +} + +gboolean fm_search_get_name_regex(FmSearch* search) +{ + return search->name_regex; +} + +void fm_search_set_name_regex(FmSearch* search, gboolean name_regex) +{ + search->name_regex = name_regex; +} + +const char* fm_search_get_content_pattern(FmSearch* search) +{ + return search->content_pattern; +} + +void fm_search_set_content_pattern(FmSearch* search, const char* content_pattern) +{ + g_free(search->content_pattern); + search->content_pattern = g_strdup(content_pattern); +} + +gboolean fm_search_get_content_ci(FmSearch* search) +{ + return search->content_ci; +} + +void fm_search_set_content_ci(FmSearch* search, gboolean content_ci) +{ + search->content_ci = content_ci; +} + +gboolean fm_search_get_content_regex(FmSearch* search) +{ + return search->content_regex; +} + +void fm_search_set_content_regex(FmSearch* search, gboolean content_regex) +{ + search->content_regex = content_regex; +} + +void fm_search_add_dir(FmSearch* search, const char* dir) +{ + GList* l = g_list_find_custom(search->search_path_list, dir, (GCompareFunc)strcmp); + if(!l) + search->search_path_list = g_list_prepend(search->search_path_list, g_strdup(dir)); +} + +void fm_search_remove_dir(FmSearch* search, const char* dir) +{ + GList* l = g_list_find_custom(search->search_path_list, dir, (GCompareFunc)strcmp); + if(G_LIKELY(l)) + { + g_free(l->data); + search->search_path_list = g_list_delete_link(search->search_path_list, l); + } +} + +GList* fm_search_get_dirs(FmSearch* search) +{ + return search->search_path_list; +} + +void fm_search_add_mime_type(FmSearch* search, const char* mime_type) +{ + GList* l = g_list_find_custom(search->mime_types, mime_type, (GCompareFunc)strcmp); + if(!l) + search->mime_types = g_list_prepend(search->mime_types, g_strdup(mime_type)); +} + +void fm_search_remove_mime_type(FmSearch* search, const char* mime_type) +{ + GList* l = g_list_find_custom(search->mime_types, mime_type, (GCompareFunc)strcmp); + if(G_LIKELY(l)) + { + g_free(l->data); + search->mime_types = g_list_delete_link(search->mime_types, l); + } +} + +GList* fm_search_get_mime_types(FmSearch* search) +{ + return search->mime_types; +} + +guint64 fm_search_get_max_size(FmSearch* search) +{ + return search->max_size; +} + +void fm_search_set_max_size(FmSearch* search, guint64 size) +{ + search->max_size = size; +} + +guint64 fm_search_get_min_size(FmSearch* search) +{ + return search->min_size; +} + +void fm_search_set_min_size(FmSearch* search, guint64 size) +{ + search->min_size = size; +} + +/* format of mtime: YYYY-MM-DD */ +const char* fm_search_get_max_mtime(FmSearch* search) +{ + return search->max_mtime; +} + +void fm_search_set_max_mtime(FmSearch* search, const char* mtime) +{ + g_free(search->max_mtime); + search->max_mtime = g_strdup(mtime); +} + +/* format of mtime: YYYY-MM-DD */ +const char* fm_search_get_min_mtime(FmSearch* search) +{ + return search->min_mtime; +} + +void fm_search_set_min_mtime(FmSearch* search, const char* mtime) +{ + g_free(search->min_mtime); + search->min_mtime = g_strdup(mtime); +} + +/* really build the path */ +FmPath* fm_search_dup_path(FmSearch* search) +{ + FmPath* search_path = NULL; + GString* search_str = g_string_sized_new(1024); + /* build the search:// URI to perform the search */ + g_string_append(search_str, "search://"); + + if(search->search_path_list) /* we need to have at least one dir path */ + { + char *escaped; + /* add paths */ + GList* l; + for(l = search->search_path_list; ; ) + { + char *path_str = (char*)l->data; + /* escape possible '?' and ',' */ + escaped = g_uri_escape_string(path_str, "!$&'()*+:;=/@", TRUE); + g_string_append(search_str, escaped); + g_free(escaped); + + l = l->next; + if(!l) /* no more items */ + break; + g_string_append_c(search_str, ','); /* separator for paths */ + } + + g_string_append_c(search_str, '?'); + g_string_append_printf(search_str, "recursive=%c", search->recursive ? '1' : '0'); + g_string_append_printf(search_str, "&show_hidden=%c", search->show_hidden ? '1' : '0'); + if(search->name_patterns && *search->name_patterns) + { + /* escape ampersands in pattern */ + escaped = g_uri_escape_string(search->name_patterns, ":/?#[]@!$'()*+,;", TRUE); + if(search->name_regex) + g_string_append_printf(search_str, "&name_regex=%s", escaped); + else + g_string_append_printf(search_str, "&name=%s", escaped); + if(search->name_ci) + g_string_append_printf(search_str, "&name_ci=%c", search->name_ci ? '1' : '0'); + g_free(escaped); + } + + if(search->content_pattern && *search->content_pattern) + { + /* escape ampersands in pattern */ + escaped = g_uri_escape_string(search->content_pattern, ":/?#[]@!$'()*+,;^<>{}", TRUE); + if(search->content_regex) + g_string_append_printf(search_str, "&content_regex=%s", escaped); + else + g_string_append_printf(search_str, "&content=%s", escaped); + g_free(escaped); + if(search->content_ci) + g_string_append_printf(search_str, "&content_ci=%c", search->content_ci ? '1' : '0'); + } + + /* search for the files of specific mime-types */ + if(search->mime_types) + { + GList* l; + g_string_append(search_str, "&mime_types="); + for(l = search->mime_types; l; l=l->next) + { + const char* mime_type = (const char*)l->data; + g_string_append(search_str, mime_type); + if(l->next) + g_string_append_c(search_str, ';'); + } + } + + if(search->min_size) + g_string_append_printf(search_str, "&min_size=%llu", search->min_size); + + if(search->max_size) + g_string_append_printf(search_str, "&max_size=%llu", search->max_size); + + if(search->min_mtime) + g_string_append_printf(search_str, "&min_mtime=%s", search->min_mtime); + + if(search->max_mtime) + g_string_append_printf(search_str, "&max_mtime=%s", search->max_mtime); + + search_path = fm_path_new_for_uri(search_str->str); + g_string_free(search_str, TRUE); + } + return search_path; +} diff --git a/libfm-qt/fm-search.h b/libfm-qt/fm-search.h new file mode 100644 index 0000000..1ae397e --- /dev/null +++ b/libfm-qt/fm-search.h @@ -0,0 +1,88 @@ +/* + * fm-search-uri.h + * + * Copyright 2015 Hong Jen Yee (PCMan) <pcman.tw@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +/* FmSearch implements a tool used to generate a search:// URI used by libfm to search for files. + * This API might become part of libfm in the future. + */ + +#ifndef _FM_SEARCH_H_ +#define _FM_SEARCH_H_ + +#include <libfm/fm.h> + +G_BEGIN_DECLS + +typedef struct _FmSearch FmSearch; + +FmSearch* fm_search_new(void); +void fm_search_free(FmSearch* search); + +FmPath* fm_search_dup_path(FmSearch* search); + +gboolean fm_search_get_recursive(FmSearch* search); +void fm_search_set_recursive(FmSearch* search, gboolean recursive); + +gboolean fm_search_get_show_hidden(FmSearch* search); +void fm_search_set_show_hidden(FmSearch* search, gboolean show_hidden); + +const char* fm_search_get_name_patterns(FmSearch* search); +void fm_search_set_name_patterns(FmSearch* search, const char* name_patterns); + +gboolean fm_search_get_name_ci(FmSearch* search); +void fm_search_set_name_ci(FmSearch* search, gboolean name_ci); + +gboolean fm_search_get_name_regex(FmSearch* search); +void fm_search_set_name_regex(FmSearch* search, gboolean name_regex); + +const char* fm_search_get_content_pattern(FmSearch* search); +void fm_search_set_content_pattern(FmSearch* search, const char* content_pattern); + +gboolean fm_search_get_content_ci(FmSearch* search); +void fm_search_set_content_ci(FmSearch* search, gboolean content_ci); + +gboolean fm_search_get_content_regex(FmSearch* search); +void fm_search_set_content_regex(FmSearch* search, gboolean content_regex); + +void fm_search_add_dir(FmSearch* search, const char* dir); +void fm_search_remove_dir(FmSearch* search, const char* dir); +GList* fm_search_get_dirs(FmSearch* search); + +void fm_search_add_mime_type(FmSearch* search, const char* mime_type); +void fm_search_remove_mime_type(FmSearch* search, const char* mime_type); +GList* fm_search_get_mime_types(FmSearch* search); + +guint64 fm_search_get_max_size(FmSearch* search); +void fm_search_set_max_size(FmSearch* search, guint64 size); + +guint64 fm_search_get_min_size(FmSearch* search); +void fm_search_set_min_size(FmSearch* search, guint64 size); + +/* format of mtime: YYYY-MM-DD */ +const char* fm_search_get_max_mtime(FmSearch* search); +void fm_search_set_max_mtime(FmSearch* search, const char* mtime); + +/* format of mtime: YYYY-MM-DD */ +const char* fm_search_get_min_mtime(FmSearch* search); +void fm_search_set_min_mtime(FmSearch* search, const char* mtime); + +G_END_DECLS + +#endif /* _FM_SEARCH_H_ */ diff --git a/libfm-qt/path.h b/libfm-qt/path.h index de6b53c..06238f0 100644 --- a/libfm-qt/path.h +++ b/libfm-qt/path.h @@ -188,6 +188,12 @@ public: return fm_path_hash(data_); } + void take(FmPath* path) { // take the ownership of the "path" + if(data_) + fm_path_unref(data_); + data_ = path; + } + Path& operator = (const Path& other) { if(data_) fm_path_unref(data_); diff --git a/libfm-qt/placesview.cpp b/libfm-qt/placesview.cpp index fc2f99a..9ea93a9 100644 --- a/libfm-qt/placesview.cpp +++ b/libfm-qt/placesview.cpp @@ -322,6 +322,8 @@ void PlacesView::onEjectVolume() { void PlacesView::contextMenuEvent(QContextMenuEvent* event) { QModelIndex index = indexAt(event->pos()); if(index.isValid() && index.parent().isValid()) { + if(index.column() != 0) // the real item is at column 0 + index = index.sibling(index.row(), 0); QMenu* menu = new QMenu(this); QAction* action; PlacesModelItem* item = static_cast<PlacesModelItem*>(model_->itemFromIndex(index)); diff --git a/libfm-qt/proxyfoldermodel.cpp b/libfm-qt/proxyfoldermodel.cpp index 963e870..ce815c6 100644 --- a/libfm-qt/proxyfoldermodel.cpp +++ b/libfm-qt/proxyfoldermodel.cpp @@ -118,6 +118,12 @@ bool ProxyFolderModel::lessThan(const QModelIndex& left, const QModelIndex& righ FmFileInfo* leftInfo = srcModel->fileInfoFromIndex(left); FmFileInfo* rightInfo = srcModel->fileInfoFromIndex(right); + if(Q_UNLIKELY(!leftInfo || !rightInfo)) { + // In theory, this should not happen, but it's safer to add the null check. + // This is reported in https://github.com/lxde/pcmanfm-qt/issues/205 + return false; + } + if(folderFirst_) { bool leftIsFolder = (bool)fm_file_info_is_dir(leftInfo); bool rightIsFolder = (bool)fm_file_info_is_dir(rightInfo); diff --git a/pcmanfm-qt.kdev4 b/pcmanfm-qt.kdev4 deleted file mode 100644 index 4588e29..0000000 --- a/pcmanfm-qt.kdev4 +++ /dev/null @@ -1,4 +0,0 @@ -[Project] -Manager=KDevCMakeManager -Name=pcmanfm-qt -VersionControl= diff --git a/pcmanfm/application.cpp b/pcmanfm/application.cpp index ffedfcd..0595b9d 100644 --- a/pcmanfm/application.cpp +++ b/pcmanfm/application.cpp @@ -42,6 +42,7 @@ #include "mountoperation.h" #include "autorundialog.h" #include "launcher.h" +#include "filesearchdialog.h" #include <QScreen> #include <QWindow> @@ -113,8 +114,8 @@ Application::~Application() { g_object_unref(volumeMonitor_); } - if(enableDesktopManager_) - removeNativeEventFilter(this); + // if(enableDesktopManager_) + // removeNativeEventFilter(this); } bool Application::parseCommandLineArgs() { @@ -311,7 +312,7 @@ void Application::desktopManager(bool enabled) { QDesktopWidget* desktopWidget = desktop(); if(enabled) { if(!enableDesktopManager_) { - installNativeEventFilter(this); + // installNativeEventFilter(this); Q_FOREACH(QScreen* screen, screens()) { connect(screen, &QScreen::virtualGeometryChanged, this, &Application::onVirtualGeometryChanged); connect(screen, &QObject::destroyed, this, &Application::onScreenDestroyed); @@ -352,7 +353,7 @@ void Application::desktopManager(bool enabled) { disconnect(screen, &QObject::destroyed, this, &Application::onScreenDestroyed); } disconnect(this, &QApplication::screenAdded, this, &Application::onScreenAdded); - removeNativeEventFilter(this); + // removeNativeEventFilter(this); } } enableDesktopManager_ = enabled; @@ -369,9 +370,22 @@ void Application::desktopPrefrences(QString page) { desktopPreferencesDialog_.data()->activateWindow(); } +void Application::onFindFileAccepted() { + Fm::FileSearchDialog* dlg = static_cast<Fm::FileSearchDialog*>(sender()); + Fm::Path uri = dlg->searchUri(); + // FIXME: we should be able to open it in an existing window + FmPathList* paths = fm_path_list_new(); + fm_path_list_push_tail(paths, uri.data()); + Launcher(NULL).launchPaths(NULL, paths); + fm_path_list_unref(paths); +} + void Application::findFiles(QStringList paths) { - // TODO: add a file searching utility here. - qDebug("findFiles"); + // launch file searching utility. + Fm::FileSearchDialog* dlg = new Fm::FileSearchDialog(paths); + connect(dlg, &QDialog::accepted, this, &Application::onFindFileAccepted); + dlg->setAttribute(Qt::WA_DeleteOnClose); + dlg->show(); } void Application::launchFiles(QString cwd, QStringList paths, bool inNewWindow) { @@ -635,17 +649,18 @@ void Application::onVolumeAdded(GVolumeMonitor* monitor, GVolume* volume, Applic pThis->autoMountVolume(volume, true); } +#if 0 bool Application::nativeEventFilter(const QByteArray & eventType, void * message, long * result) { if(eventType == "xcb_generic_event_t") { // XCB event // filter all native X11 events (xcb) xcb_generic_event_t* generic_event = reinterpret_cast<xcb_generic_event_t*>(message); // qDebug("XCB event: %d", generic_event->response_type & ~0x80); Q_FOREACH(DesktopWindow * window, desktopWindows_) { - window->xcbEvent(generic_event); } } return false; } +#endif void Application::onScreenAdded(QScreen* newScreen) { if(enableDesktopManager_) { diff --git a/pcmanfm/application.h b/pcmanfm/application.h index c0dc727..ccc80e4 100644 --- a/pcmanfm/application.h +++ b/pcmanfm/application.h @@ -25,7 +25,6 @@ #include "settings.h" #include "libfmqt.h" #include "editbookmarksdialog.h" -#include <QAbstractNativeEventFilter> #include <QVector> #include <QPointer> #include <QProxyStyle> @@ -49,7 +48,7 @@ public: virtual int styleHint(StyleHint hint, const QStyleOption * option = 0, const QWidget * widget = 0, QStyleHintReturn * returnData = 0) const; }; -class Application : public QApplication, public QAbstractNativeEventFilter { +class Application : public QApplication { Q_OBJECT Q_PROPERTY(bool desktopManagerEnabled READ desktopManagerEnabled) @@ -75,7 +74,7 @@ public: void desktopPrefrences(QString page); void editBookmarks(); void desktopManager(bool enabled); - void findFiles(QStringList paths); + void findFiles(QStringList paths = QStringList()); bool desktopManagerEnabled() { return enableDesktopManager_; @@ -91,8 +90,6 @@ public: return profileName_; } - virtual bool nativeEventFilter(const QByteArray & eventType, void * message, long * result); - protected Q_SLOTS: void onAboutToQuit(); void onSigtermNotified(); @@ -108,6 +105,8 @@ protected Q_SLOTS: void onScreenAdded(QScreen* newScreen); void reloadDesktopsAsNeeded(); + void onFindFileAccepted(); + protected: virtual bool eventFilter(QObject* watched, QEvent* event); bool parseCommandLineArgs(); diff --git a/pcmanfm/desktop-preferences.ui b/pcmanfm/desktop-preferences.ui index 7182bd5..258cd95 100644 --- a/pcmanfm/desktop-preferences.ui +++ b/pcmanfm/desktop-preferences.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>473</width> - <height>428</height> + <width>501</width> + <height>376</height> </rect> </property> <property name="windowTitle"> @@ -200,6 +200,19 @@ </layout> </widget> </item> + <item> + <spacer name="verticalSpacer_2"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>10</height> + </size> + </property> + </spacer> + </item> </layout> </widget> <widget class="QWidget" name="advancedPage"> diff --git a/pcmanfm/desktopwindow.cpp b/pcmanfm/desktopwindow.cpp index 8c3260e..effb80d 100644 --- a/pcmanfm/desktopwindow.cpp +++ b/pcmanfm/desktopwindow.cpp @@ -389,49 +389,6 @@ void DesktopWindow::prepareFolderMenu(Fm::FolderMenu* menu) { connect(action, &QAction::triggered, this, &DesktopWindow::onDesktopPreferences); } -void DesktopWindow::xcbEvent(xcb_generic_event_t* generic_event) { - int event_type = generic_event->response_type & ~0x80; - if(showWmMenu_) { - // If we want to show the desktop menus provided by the window manager instead of ours, - // we have to forward the mouse events we received to the root window. - switch(event_type) { - case XCB_BUTTON_PRESS: { - xcb_button_press_event_t* event = reinterpret_cast<xcb_button_press_event_t*>(generic_event); - if(event->event == effectiveWinId()) { - // check if the user click on blank area - QModelIndex index = listView_->indexAt(QPoint(event->event_x, event->event_y)); - if(!index.isValid()) { - xcb_ungrab_pointer(QX11Info::connection(), event->time); - // forward the event to the root window - xcb_button_press_event_t event2 = *event; - WId root = QX11Info::appRootWindow(QX11Info::appScreen()); - event2.event = root; - xcb_send_event(QX11Info::connection(), 0, root, XCB_EVENT_MASK_BUTTON_PRESS, (char*)&event2); - } - } - break; - } - case XCB_BUTTON_RELEASE: { - xcb_button_release_event_t* event = reinterpret_cast<xcb_button_release_event_t*>(generic_event); - if(event->event == effectiveWinId()) { - // check if the user click on blank area - QModelIndex index = listView_->indexAt(QPoint(event->event_x, event->event_y)); - if(!index.isValid()) { - // forward the event to the root window - xcb_button_release_event_t event2 = *event; - WId root = QX11Info::appRootWindow(QX11Info::appScreen()); - event2.event = root; - xcb_send_event(QX11Info::connection(), 0, root, XCB_EVENT_MASK_BUTTON_RELEASE, (char*)&event2); - } - } - break; - } - default: - break; - } - } -} - void DesktopWindow::onDesktopPreferences() { static_cast<Application* >(qApp)->desktopPrefrences(QString()); } @@ -675,6 +632,69 @@ void DesktopWindow::onFilePropertiesActivated() { } } +static void forwardMouseEventToRoot(QMouseEvent* event) { + xcb_ungrab_pointer(QX11Info::connection(), event->timestamp()); + // forward the event to the root window + xcb_button_press_event_t xcb_event; + uint32_t mask = 0; + xcb_event.state = 0; + switch(event->type()) { + case QEvent::MouseButtonPress: + xcb_event.response_type = XCB_BUTTON_PRESS; + mask = XCB_EVENT_MASK_BUTTON_PRESS; + break; + case QEvent::MouseButtonRelease: + xcb_event.response_type = XCB_BUTTON_RELEASE; + mask = XCB_EVENT_MASK_BUTTON_RELEASE; + break; + default: + return; + } + + // convert Qt button to XCB button + switch(event->button()) { + case Qt::LeftButton: + xcb_event.detail = 1; + xcb_event.state |= XCB_BUTTON_MASK_1; + break; + case Qt::MiddleButton: + xcb_event.detail = 2; + xcb_event.state |= XCB_BUTTON_MASK_2; + break; + case Qt::RightButton: + xcb_event.detail = 3; + xcb_event.state |= XCB_BUTTON_MASK_3; + break; + default: + xcb_event.detail = 0; + } + + // convert Qt modifiers to XCB states + if(event->modifiers() & Qt::ShiftModifier) + xcb_event.state |= XCB_MOD_MASK_SHIFT; + if(event->modifiers() & Qt::ControlModifier) + xcb_event.state |= XCB_MOD_MASK_SHIFT; + if(event->modifiers() & Qt::AltModifier) + xcb_event.state |= XCB_MOD_MASK_1; + + xcb_event.sequence = 0; + xcb_event.time = event->timestamp(); + + WId root = QX11Info::appRootWindow(QX11Info::appScreen()); + xcb_event.event = root; + xcb_event.root = root; + xcb_event.child = 0; + + xcb_event.root_x = event->globalX(); + xcb_event.root_y = event->globalY(); + xcb_event.event_x = event->x(); + xcb_event.event_y = event->y(); + xcb_event.same_screen = 1; + + xcb_send_event(QX11Info::connection(), 0, root, mask, (char*)&xcb_event); + xcb_flush(QX11Info::connection()); +} + bool DesktopWindow::event(QEvent* event) { switch(event->type()) { @@ -721,6 +741,25 @@ bool DesktopWindow::eventFilter(QObject * watched, QEvent * event) { break; } } + else if(watched == listView_->viewport()) { + switch(event->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + if(showWmMenu_) { + QMouseEvent* e = static_cast<QMouseEvent*>(event); + // If we want to show the desktop menus provided by the window manager instead of ours, + // we have to forward the mouse events we received to the root window. + // check if the user click on blank area + QModelIndex index = listView_->indexAt(e->pos()); + if(!index.isValid() && e->button() != Qt::LeftButton) { + forwardMouseEventToRoot(e); + } + } + break; + default: + break; + } + } return false; } diff --git a/pcmanfm/desktopwindow.h b/pcmanfm/desktopwindow.h index f1ad12b..c28e322 100644 --- a/pcmanfm/desktopwindow.h +++ b/pcmanfm/desktopwindow.h @@ -66,8 +66,6 @@ public: void updateWallpaper(); void updateFromSettings(Settings& settings); - void xcbEvent(xcb_generic_event_t* generic_event); - void queueRelayout(int delay = 0); int screenNum() const { diff --git a/pcmanfm/file-search.ui b/pcmanfm/file-search.ui index a698eae..20dc03c 100644 --- a/pcmanfm/file-search.ui +++ b/pcmanfm/file-search.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>431</width> - <height>359</height> + <height>416</height> </rect> </property> <property name="windowTitle"> @@ -76,7 +76,9 @@ <string>Add</string> </property> <property name="icon"> - <iconset theme="list-add"/> + <iconset theme="list-add"> + <normaloff/> + </iconset> </property> </widget> </item> @@ -86,7 +88,9 @@ <string>Remove</string> </property> <property name="icon"> - <iconset theme="list-remove"/> + <iconset theme="list-remove"> + <normaloff/> + </iconset> </property> </widget> </item> @@ -125,9 +129,6 @@ </widget> </item> </layout> - <zorder>buttonBox</zorder> - <zorder>groupBox</zorder> - <zorder>groupBox_2</zorder> </widget> <widget class="QWidget" name="tab_2"> <attribute name="title"> @@ -256,7 +257,7 @@ <property name="title"> <string>File Size</string> </property> - <layout class="QGridLayout" name="gridLayout"> + <layout class="QFormLayout" name="formLayout_3"> <item row="0" column="0"> <widget class="QCheckBox" name="checkBox_12"> <property name="text"> @@ -265,18 +266,22 @@ </widget> </item> <item row="0" column="1"> - <widget class="QSpinBox" name="spinBox"> - <property name="enabled"> - <bool>false</bool> - </property> - </widget> - </item> - <item row="0" column="2"> - <widget class="QComboBox" name="comboBox"> - <property name="enabled"> - <bool>false</bool> - </property> - </widget> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QSpinBox" name="spinBox"> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="comboBox"> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> </item> <item row="1" column="0"> <widget class="QCheckBox" name="checkBox_13"> @@ -286,18 +291,22 @@ </widget> </item> <item row="1" column="1"> - <widget class="QSpinBox" name="spinBox_2"> - <property name="enabled"> - <bool>false</bool> - </property> - </widget> - </item> - <item row="1" column="2"> - <widget class="QComboBox" name="comboBox_2"> - <property name="enabled"> - <bool>false</bool> - </property> - </widget> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QSpinBox" name="spinBox_2"> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="comboBox_2"> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> </item> </layout> </widget> @@ -307,7 +316,7 @@ <property name="title"> <string>Last Modified Time</string> </property> - <layout class="QGridLayout" name="gridLayout_2"> + <layout class="QFormLayout" name="formLayout_2"> <item row="0" column="0"> <widget class="QCheckBox" name="checkBox_14"> <property name="text"> @@ -315,6 +324,16 @@ </property> </widget> </item> + <item row="0" column="1"> + <widget class="QDateTimeEdit" name="dateTimeEdit"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="calendarPopup"> + <bool>true</bool> + </property> + </widget> + </item> <item row="1" column="0"> <widget class="QCheckBox" name="checkBox_15"> <property name="text"> @@ -332,16 +351,6 @@ </property> </widget> </item> - <item row="0" column="1"> - <widget class="QDateTimeEdit" name="dateTimeEdit"> - <property name="enabled"> - <bool>false</bool> - </property> - <property name="calendarPopup"> - <bool>true</bool> - </property> - </widget> - </item> </layout> </widget> </item> diff --git a/pcmanfm/main-win.ui b/pcmanfm/main-win.ui index 13740b1..408c4a6 100644 --- a/pcmanfm/main-win.ui +++ b/pcmanfm/main-win.ui @@ -20,7 +20,16 @@ </property> <widget class="QWidget" name="centralwidget"> <layout class="QVBoxLayout" name="verticalLayout"> - <property name="margin"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> <number>0</number> </property> <item> @@ -67,7 +76,7 @@ <x>0</x> <y>0</y> <width>460</width> - <height>27</height> + <height>23</height> </rect> </property> <widget class="QMenu" name="menu_File"> @@ -171,6 +180,7 @@ </property> <addaction name="actionOpenTerminal"/> <addaction name="actionOpenAsRoot"/> + <addaction name="actionFindFiles"/> </widget> <addaction name="menu_File"/> <addaction name="menu_Editw"/> @@ -227,7 +237,7 @@ </iconset> </property> <property name="text"> - <string>Home</string> + <string>&Home</string> </property> <property name="shortcut"> <string>Alt+Home</string> @@ -447,7 +457,7 @@ <bool>true</bool> </property> <property name="text"> - <string>Ascending</string> + <string>&Ascending</string> </property> </action> <action name="actionDescending"> @@ -455,7 +465,7 @@ <bool>true</bool> </property> <property name="text"> - <string>Descending</string> + <string>&Descending</string> </property> </action> <action name="actionByFileName"> @@ -463,7 +473,7 @@ <bool>true</bool> </property> <property name="text"> - <string>By File Name</string> + <string>&By File Name</string> </property> </action> <action name="actionByMTime"> @@ -471,7 +481,7 @@ <bool>true</bool> </property> <property name="text"> - <string>By Modification Time</string> + <string>By &Modification Time</string> </property> </action> <action name="actionByFileType"> @@ -479,7 +489,7 @@ <bool>true</bool> </property> <property name="text"> - <string>By File Type</string> + <string>By File &Type</string> </property> </action> <action name="actionByOwner"> @@ -487,7 +497,7 @@ <bool>true</bool> </property> <property name="text"> - <string>By Owner</string> + <string>By &Owner</string> </property> </action> <action name="actionFolderFirst"> @@ -495,7 +505,7 @@ <bool>true</bool> </property> <property name="text"> - <string>Folder First</string> + <string>&Folder First</string> </property> </action> <action name="actionNewTab"> @@ -598,7 +608,7 @@ <bool>true</bool> </property> <property name="text"> - <string>Case Sensitive</string> + <string>&Case Sensitive</string> </property> </action> <action name="actionByFileSize"> @@ -606,12 +616,12 @@ <bool>true</bool> </property> <property name="text"> - <string>By File Size</string> + <string>By File &Size</string> </property> </action> <action name="actionCloseWindow"> <property name="text"> - <string>Close Window</string> + <string>&Close Window</string> </property> </action> <action name="actionEdit_Bookmarks"> @@ -639,10 +649,12 @@ </action> <action name="actionNewFolder"> <property name="icon"> - <iconset theme="folder-new"/> + <iconset theme="folder-new"> + <normaloff/> + </iconset> </property> <property name="text"> - <string>Folder</string> + <string>&Folder</string> </property> <property name="shortcut"> <string>Ctrl+Shift+N</string> @@ -650,15 +662,25 @@ </action> <action name="actionNewBlankFile"> <property name="icon"> - <iconset theme="document-new"/> + <iconset theme="document-new"> + <normaloff/> + </iconset> </property> <property name="text"> - <string>Blank File</string> + <string>&Blank File</string> </property> <property name="shortcut"> <string>Ctrl+Alt+N</string> </property> </action> + <action name="actionFindFiles"> + <property name="text"> + <string>&Find Files</string> + </property> + <property name="shortcut"> + <string>F3</string> + </property> + </action> </widget> <customwidgets> <customwidget> diff --git a/pcmanfm/mainwindow.cpp b/pcmanfm/mainwindow.cpp index 19c22f9..1276d30 100644 --- a/pcmanfm/mainwindow.cpp +++ b/pcmanfm/mainwindow.cpp @@ -949,6 +949,25 @@ void MainWindow::on_actionOpenAsRoot_triggered() { } } +void MainWindow::on_actionFindFiles_triggered() { + Application* app = static_cast<Application*>(qApp); + FmPathList* selectedPaths = currentPage()->selectedFilePaths(); + QStringList paths; + if(selectedPaths) { + for(GList* l = fm_path_list_peek_head_link(selectedPaths); l; l = l->next) { + // FIXME: is it ok to use display name here? + // This might be broken on filesystems with non-UTF-8 filenames. + Fm::Path path(FM_PATH(l->data)); + paths.append(path.displayName(false)); + } + fm_path_list_unref(selectedPaths); + } + else { + paths.append(currentPage()->pathName()); + } + app->findFiles(paths); +} + void MainWindow::on_actionOpenTerminal_triggered() { TabPage* page = currentPage(); if(page) { diff --git a/pcmanfm/mainwindow.h b/pcmanfm/mainwindow.h index 58b6aa6..a36ec93 100644 --- a/pcmanfm/mainwindow.h +++ b/pcmanfm/mainwindow.h @@ -109,6 +109,7 @@ protected Q_SLOTS: void on_actionOpenTerminal_triggered(); void on_actionOpenAsRoot_triggered(); + void on_actionFindFiles_triggered(); void on_actionAbout_triggered();