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 @@
+
+
+ SearchDialog
+
+
+
+ 0
+ 0
+ 512
+ 420
+
+
+
+ Search Files
+
+
+
+
+
+
+
+ -
+
+
+ 0
+
+
+
+ Name/Location
+
+
+
-
+
+
+ File Name Patterns:
+
+
+
-
+
+
+ *
+
+
+
+ -
+
+
+ Case insensitive
+
+
+
+ -
+
+
+ Use regular expression
+
+
+
+
+
+
+ -
+
+
+ Places to Search:
+
+
+
-
+
+
-
+
+
+ -
+
+
-
+
+
+ &Add
+
+
+
+
+
+
+
+
+ -
+
+
+ &Remove
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+ -
+
+
+ Search in sub directories
+
+
+
+ -
+
+
+ Search for hidden files
+
+
+
+
+
+
+
+
+
+
+ File Type
+
+
+ -
+
+
+ Only search for files of following types:
+
+
+
-
+
+
+ Text files
+
+
+
+ -
+
+
+ Image files
+
+
+
+ -
+
+
+ Audio files
+
+
+
+ -
+
+
+ Video files
+
+
+
+ -
+
+
+ Documents
+
+
+
+ -
+
+
+ Folders
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+ Content
+
+
+ -
+
+
+ File contains:
+
+
+
-
+
+
+ -
+
+
+ Case insensiti&ve
+
+
+
+ -
+
+
+ &Use regular expression
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 186
+
+
+
+
+
+
+
+
+ Properties
+
+
+ -
+
+
+ File Size:
+
+
+
-
+
+
+ Larger than:
+
+
+
+ -
+
+
-
+
+
+ -
+
+
+ 2
+
+
-
+
+ Bytes
+
+
+ -
+
+ KiB
+
+
+ -
+
+ MiB
+
+
+ -
+
+ GiB
+
+
+
+
+
+
+ -
+
+
+ Smaller than:
+
+
+
+ -
+
+
-
+
+
+ -
+
+
+ 2
+
+
-
+
+ Bytes
+
+
+ -
+
+ KiB
+
+
+ -
+
+ MiB
+
+
+ -
+
+ GiB
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Last Modified Time:
+
+
+
-
+
+
+ Earlier than:
+
+
+
+ -
+
+
+ Later than:
+
+
+
+ -
+
+
+ true
+
+
+
+ -
+
+
+ true
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ SearchDialog
+ accept()
+
+
+ 248
+ 254
+
+
+ 157
+ 274
+
+
+
+
+ buttonBox
+ rejected()
+ SearchDialog
+ reject()
+
+
+ 316
+ 260
+
+
+ 286
+ 274
+
+
+
+
+
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)
+ *
+ * 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
+#include "fm-search.h"
+#include "ui_filesearch.h"
+#include
+#include
+
+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().max());
+ ui->maxSize->setMaximum(std::numeric_limits().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)
+ *
+ * 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
+#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)
+ * Copyright 2012-2014 Andriy Grytsenko (LStranger)
+ *
+ * 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
+
+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)
+ *
+ * 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
+
+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(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
#include
@@ -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(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(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
#include
#include
#include
@@ -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 @@
0
0
- 473
- 428
+ 501
+ 376
@@ -200,6 +200,19 @@
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 10
+
+
+
+
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(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(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(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(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 @@
0
0
431
- 359
+ 416
@@ -76,7 +76,9 @@
Add
-
+
+
+
@@ -86,7 +88,9 @@
Remove
-
+
+
+
@@ -125,9 +129,6 @@
- buttonBox
- groupBox
- groupBox_2
@@ -256,7 +257,7 @@
File Size
-
+
-
@@ -265,18 +266,22 @@
-
-
-
- false
-
-
-
- -
-
-
- false
-
-
+
+
-
+
+
+ false
+
+
+
+ -
+
+
+ false
+
+
+
+
-
@@ -286,18 +291,22 @@
-
-
-
- false
-
-
-
- -
-
-
- false
-
-
+
+
-
+
+
+ false
+
+
+
+ -
+
+
+ false
+
+
+
+
@@ -307,7 +316,7 @@
Last Modified Time
-
+
-
@@ -315,6 +324,16 @@
+ -
+
+
+ false
+
+
+ true
+
+
+
-
@@ -332,16 +351,6 @@
- -
-
-
- false
-
-
- true
-
-
-
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 @@
-
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
0
-
@@ -67,7 +76,7 @@
0
0
460
- 27
+ 23
@@ -227,7 +237,7 @@
- Home
+ &Home
Alt+Home
@@ -447,7 +457,7 @@
true
- Ascending
+ &Ascending
@@ -455,7 +465,7 @@
true
- Descending
+ &Descending
@@ -463,7 +473,7 @@
true
- By File Name
+ &By File Name
@@ -471,7 +481,7 @@
true
- By Modification Time
+ By &Modification Time
@@ -479,7 +489,7 @@
true
- By File Type
+ By File &Type
@@ -487,7 +497,7 @@
true
- By Owner
+ By &Owner
@@ -495,7 +505,7 @@
true
- Folder First
+ &Folder First
@@ -598,7 +608,7 @@
true
- Case Sensitive
+ &Case Sensitive
@@ -606,12 +616,12 @@
true
- By File Size
+ By File &Size
- Close Window
+ &Close Window
@@ -639,10 +649,12 @@
-
+
+
+
- Folder
+ &Folder
Ctrl+Shift+N
@@ -650,15 +662,25 @@
-
+
+
+
- Blank File
+ &Blank File
Ctrl+Alt+N
+
+
+ &Find Files
+
+
+ F3
+
+
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(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();