From 2d86a13669186e03a891f6d2b7c58ca289b1af6c Mon Sep 17 00:00:00 2001 From: Alf Gaida Date: Wed, 17 Feb 2016 01:32:25 +0100 Subject: [PATCH 1/2] Adding upstream version 0.10.0+20160119. --- CMakeLists.txt | 1 + src/CMakeLists.txt | 3 + src/filesearch.ui | 120 +++++++++++- src/filesearchdialog.cpp | 2 +- src/folderitemdelegate.cpp | 14 +- src/foldermodel.cpp | 5 + src/folderview.cpp | 52 +++++- src/folderview.h | 12 +- src/libfmqt.cpp | 2 + src/pathedit.cpp | 37 ++++ src/pathedit.h | 2 + src/thumbnailloader.cpp | 8 +- src/translations/libfm-qt_cs_CZ.ts | 154 ++++++++-------- src/translations/libfm-qt_de.ts | 74 ++++---- src/translations/libfm-qt_lt_LT.ts | 2 +- src/xdndworkaround.cpp | 286 +++++++++++++++++++++++++++++ src/xdndworkaround.h | 90 +++++++++ 17 files changed, 739 insertions(+), 125 deletions(-) create mode 100644 src/xdndworkaround.cpp create mode 100644 src/xdndworkaround.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b254f4..2241ded 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,6 +38,7 @@ pkg_check_modules(SYSTEM_LIBS REQUIRED glib-2.0 gio-2.0 gio-unix-2.0 + xcb ) pkg_check_modules(LIBFM REQUIRED libfm>="${REQUIRED_LIBFM_VERSION}") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1264952..14cd663 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -58,6 +58,7 @@ set(libfm_SRCS appchooserdialog.cpp filesearchdialog.cpp fm-search.c # might be moved to libfm later + xdndworkaround.cpp ) set(libfm_UIS @@ -118,6 +119,8 @@ set_target_properties(${LIBFM_QT_LIBRARY_NAME} PROPERTIES ) target_include_directories(${LIBFM_QT_LIBRARY_NAME} + PRIVATE "${LIB_XCB_INDLUDE_DIRS}" + PRIVATE "${Qt5Gui_PRIVATE_INCLUDE_DIRS}" INTERFACE "$" ) diff --git a/src/filesearch.ui b/src/filesearch.ui index f5f6051..1cb466f 100644 --- a/src/filesearch.ui +++ b/src/filesearch.ui @@ -270,10 +270,17 @@ - + + + false + + + + false + 2 @@ -311,10 +318,17 @@ - + + + false + + + + false + 2 @@ -367,6 +381,9 @@ + + false + true @@ -374,6 +391,9 @@ + + false + true @@ -445,5 +465,101 @@ + + largerThan + toggled(bool) + minSizeUnit + setEnabled(bool) + + + 93 + 84 + + + 403 + 88 + + + + + smallerThan + toggled(bool) + maxSize + setEnabled(bool) + + + 96 + 119 + + + 241 + 123 + + + + + largerThan + toggled(bool) + minSize + setEnabled(bool) + + + 93 + 84 + + + 241 + 88 + + + + + smallerThan + toggled(bool) + maxSizeUnit + setEnabled(bool) + + + 96 + 119 + + + 403 + 123 + + + + + laterThan + toggled(bool) + minTime + setEnabled(bool) + + + 88 + 223 + + + 319 + 226 + + + + + earlierThan + toggled(bool) + maxTime + setEnabled(bool) + + + 93 + 190 + + + 319 + 193 + + + diff --git a/src/filesearchdialog.cpp b/src/filesearchdialog.cpp index eeaeaa3..022747e 100644 --- a/src/filesearchdialog.cpp +++ b/src/filesearchdialog.cpp @@ -101,7 +101,7 @@ void FileSearchDialog::accept() { if(ui->smallerThan->isChecked()) { guint64 size = ui->maxSize->value() * unit_bytes[ui->maxSizeUnit->currentIndex()]; - fm_search_set_min_size(search, size); + fm_search_set_max_size(search, size); } // search based on file mtime (we only support date in YYYY-MM-DD format) diff --git a/src/folderitemdelegate.cpp b/src/folderitemdelegate.cpp index d1cfe66..078345c 100644 --- a/src/folderitemdelegate.cpp +++ b/src/folderitemdelegate.cpp @@ -178,12 +178,24 @@ void FolderItemDelegate::drawText(QPainter* painter, QStyleOptionViewItemV4& opt QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; if(opt.state & QStyle::State_Selected) { - painter->fillRect(selRect, opt.palette.highlight()); + if(!opt.widget) + painter->fillRect(selRect, opt.palette.highlight()); painter->setPen(opt.palette.color(cg, QPalette::HighlightedText)); } else painter->setPen(opt.palette.color(cg, QPalette::Text)); + if (opt.state & QStyle::State_Selected || opt.state & QStyle::State_MouseOver) { + if (const QWidget* widget = opt.widget) { // let the style engine do it + QStyle* style = widget->style() ? widget->style() : qApp->style(); + QStyleOptionViewItemV4 o(opt); + o.text = QString(); + o.rect = selRect.toAlignedRect().intersected(opt.rect); // due to clipping and rounding, we might lose 1px + o.showDecorationSelected = true; + style->drawPrimitive(QStyle::PE_PanelItemViewItem, &o, painter, widget); + } + } + // draw text for(int i = 0; i < visibleLines; ++i) { QTextLine line = layout.lineAt(i); diff --git a/src/foldermodel.cpp b/src/foldermodel.cpp index 1d90076..de13b77 100644 --- a/src/foldermodel.cpp +++ b/src/foldermodel.cpp @@ -346,6 +346,11 @@ QStringList FolderModel::mimeTypes() const { qDebug("FolderModel::mimeTypes"); QStringList types = QAbstractItemModel::mimeTypes(); // now types contains "application/x-qabstractitemmodeldatalist" + + // add support for freedesktop Xdnd direct save (XDS) protocol. + // http://www.freedesktop.org/wiki/Specifications/XDS/#index4h2 + // the real implementation is in FolderView::childDropEvent(). + types << "XdndDirectSave0"; types << "text/uri-list"; // types << "x-special/gnome-copied-files"; return types; diff --git a/src/folderview.cpp b/src/folderview.cpp index d47974c..6814e49 100644 --- a/src/folderview.cpp +++ b/src/folderview.cpp @@ -37,6 +37,10 @@ #include #include #include +#include // for XDS support +#include // for XDS support +#include "xdndworkaround.h" // for XDS support +#include "path.h" #include "folderview_p.h" Q_DECLARE_OPAQUE_POINTER(FmFileInfo*) @@ -389,7 +393,8 @@ FolderView::FolderView(ViewMode _mode, QWidget* parent): autoSelectionTimer_(nullptr), selChangedTimer_(nullptr), fileLauncher_(nullptr), - model_(nullptr) { + model_(nullptr), + itemDelegateMargins_(QSize(3, 3)) { iconSize_[IconMode - FirstViewMode] = QSize(48, 48); iconSize_[CompactMode - FirstViewMode] = QSize(24, 24); @@ -577,7 +582,7 @@ void FolderView::updateGridSize() { ; // do not use grid size } if(mode == IconMode || mode == ThumbnailMode) - listView->setGridSize(grid + QSize(6, 6)); // a margin of 6 px for every cell + listView->setGridSize(grid + 2 * itemDelegateMargins_); // the default spacing is 6(=2x3) px else listView->setGridSize(grid); FolderItemDelegate* delegate = static_cast(listView->itemDelegateForColumn(FolderModel::ColumnFileName)); @@ -600,6 +605,13 @@ QSize FolderView::iconSize(ViewMode mode) const { return iconSize_[mode - FirstViewMode]; } +void FolderView::setMargins(QSize size) { + if (itemDelegateMargins_ != size.expandedTo(QSize(0, 0))) { + itemDelegateMargins_ = size.expandedTo(QSize(0, 0)); + updateGridSize(); + } +} + FolderView::ViewMode FolderView::viewMode() const { return mode; } @@ -800,7 +812,41 @@ void FolderView::childDragMoveEvent(QDragMoveEvent* e) { } void FolderView::childDropEvent(QDropEvent* e) { - qDebug("drop"); + // qDebug("drop"); + // Try to support XDS + // NOTE: in theory, it's not possible to implement XDS with pure Qt. + // We achieved this with some dirty XCB/XDND workarounds. + // Please refer to XdndWorkaround::clientMessage() in xdndworkaround.cpp for details. + if(QX11Info::isPlatformX11() && e->mimeData()->hasFormat("XdndDirectSave0")) { + e->setDropAction(Qt::CopyAction); + const QWidget* targetWidget = childView()->viewport(); + // these are dynamic QObject property set by our XDND workarounds in xworkaround.cpp. + xcb_window_t dndSource = xcb_window_t(targetWidget->property("xdnd::lastDragSource").toUInt()); + xcb_timestamp_t dropTimestamp = (xcb_timestamp_t)targetWidget->property("xdnd::lastDropTime").toUInt(); + // qDebug() << "XDS: source window" << dndSource << dropTimestamp; + if(dndSource != 0) { + xcb_connection_t* conn = QX11Info::connection(); + xcb_atom_t XdndDirectSaveAtom = XdndWorkaround::internAtom("XdndDirectSave0", 15); + xcb_atom_t textAtom = XdndWorkaround::internAtom("text/plain", 10); + + // 1. get the filename from XdndDirectSave property of the source window + QByteArray basename = XdndWorkaround::windowProperty(dndSource, XdndDirectSaveAtom, textAtom, 1024); + + // 2. construct the fill URI for the file, and update the source window property. + Path filePath = Path(path()).child(basename); + QByteArray fileUri = filePath.toUri(); + XdndWorkaround::setWindowProperty(dndSource, XdndDirectSaveAtom, textAtom, (void*)fileUri.constData(), fileUri.length()); + + // 3. send to XDS selection data request with type "XdndDirectSave" to the source window and + // receive result from the source window. (S: success, E: error, or F: failure) + QByteArray result = e->mimeData()->data("XdndDirectSave0"); + // NOTE: there seems to be some bugs in file-roller so it always replies with "E" even if the + // file extraction is finished successfully. Anyways, we ignore any error at the moment. + } + e->accept(); // yeah! we've done with XDS so stop Qt from further event propagation. + return; + } + if(e->keyboardModifiers() == Qt::NoModifier) { // if no key modifiers are used, popup a menu // to ask the user for the action he/she wants to perform. diff --git a/src/folderview.h b/src/folderview.h index 1ae2e49..cb21667 100644 --- a/src/folderview.h +++ b/src/folderview.h @@ -133,7 +133,15 @@ protected: virtual bool eventFilter(QObject* watched, QEvent* event); - void updateGridSize(); // called when view mode, icon size, or font size is changed + void updateGridSize(); // called when view mode, icon size, font size or cell margin is changed + + QSize getMargins() const { + return itemDelegateMargins_; + } + + // sets the cell margins in the icon and thumbnail modes + // and calls updateGridSize() when needed + void setMargins(QSize size); public Q_SLOTS: void onItemActivated(QModelIndex index); @@ -162,6 +170,8 @@ private: QTimer* autoSelectionTimer_; QModelIndex lastAutoSelectionIndex_; QTimer* selChangedTimer_; + // the cell margins in the icon and thumbnail modes + QSize itemDelegateMargins_; }; } diff --git a/src/libfmqt.cpp b/src/libfmqt.cpp index c874150..aa5f2d5 100644 --- a/src/libfmqt.cpp +++ b/src/libfmqt.cpp @@ -22,6 +22,7 @@ #include #include "icontheme.h" #include "thumbnailloader.h" +#include "xdndworkaround.h" namespace Fm { @@ -32,6 +33,7 @@ struct LibFmQtData { IconTheme* iconTheme; ThumbnailLoader* thumbnailLoader; QTranslator translator; + XdndWorkaround workaround; int refCount; Q_DISABLE_COPY(LibFmQtData) }; diff --git a/src/pathedit.cpp b/src/pathedit.cpp index 4e30ca6..01fd35a 100644 --- a/src/pathedit.cpp +++ b/src/pathedit.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include namespace Fm { @@ -98,6 +99,22 @@ void PathEdit::focusOutEvent(QFocusEvent* e) { freeCompleter(); } +bool PathEdit::event(QEvent* e) { + // Stop Qt from moving the keyboard focus to the next widget when "Tab" is pressed. + // Instead, we need to do auto-completion in this case. + if(e->type() == QEvent::KeyPress) { + QKeyEvent* keyEvent = static_cast(e); + if(keyEvent->key() == Qt::Key_Tab && keyEvent->modifiers() == Qt::NoModifier) { // Tab key is pressed + e->accept(); + // do auto-completion when the user press the Tab key. + // This fixes #201: https://github.com/lxde/pcmanfm-qt/issues/201 + autoComplete(); + return true; + } + } + return QLineEdit::event(e); +} + void PathEdit::onTextChanged(const QString& text) { int pos = text.lastIndexOf('/'); if(pos >= 0) @@ -115,6 +132,26 @@ void PathEdit::onTextChanged(const QString& text) { } } +void PathEdit::autoComplete() { + // find longest common prefix of the strings currently shown in the candidate list + QAbstractItemModel* model = completer_->completionModel(); + if(model->rowCount() > 0) { + int minLen = text().length(); + QString commonPrefix = model->data(model->index(0, 0)).toString(); + for(int row = 1; row < model->rowCount() && commonPrefix.length() > minLen; ++row) { + QModelIndex index = model->index(row, 0); + QString rowText = model->data(index).toString(); + int prefixLen = 0; + while(prefixLen < rowText.length() && prefixLen < commonPrefix.length() && rowText[prefixLen] == commonPrefix[prefixLen]) { + ++prefixLen; + } + commonPrefix.truncate(prefixLen); + } + if(commonPrefix.length() > minLen) { + setText(commonPrefix); + } + } +} void PathEdit::reloadCompleter(bool triggeredByFocusInEvent) { // parent dir has been changed, reload dir list diff --git a/src/pathedit.h b/src/pathedit.h index 2c10d5f..950bcf6 100644 --- a/src/pathedit.h +++ b/src/pathedit.h @@ -41,11 +41,13 @@ public: protected: virtual void focusInEvent(QFocusEvent* e); virtual void focusOutEvent(QFocusEvent* e); + virtual bool event(QEvent* e); private Q_SLOTS: void onTextChanged(const QString & text); private: + void autoComplete(); void reloadCompleter(bool triggeredByFocusInEvent = false); void freeCompleter(); void onJobFinished(); diff --git a/src/thumbnailloader.cpp b/src/thumbnailloader.cpp index 52c03a5..af60fd6 100644 --- a/src/thumbnailloader.cpp +++ b/src/thumbnailloader.cpp @@ -21,6 +21,7 @@ #include "thumbnailloader.h" #include #include +#include namespace Fm { @@ -114,8 +115,8 @@ GObject* ThumbnailLoader::readImageFromFile(const char* filename) { GObject* ThumbnailLoader::readImageFromStream(GInputStream* stream, guint64 len, GCancellable* cancellable) { // qDebug("readImageFromStream: %p, %llu", stream, len); // FIXME: should we set a limit here? Otherwise if len is too large, we can run out of memory. - unsigned char* buffer = new unsigned char[len]; // allocate enough buffer - unsigned char* pbuffer = buffer; + QScopedArrayPointer buffer(new unsigned char[len]); // allocate enough buffer + unsigned char* pbuffer = buffer.data(); int totalReadSize = 0; while(!g_cancellable_is_cancelled(cancellable) && totalReadSize < len) { int bytesToRead = totalReadSize + 4096 > len ? len - totalReadSize : 4096; @@ -128,8 +129,7 @@ GObject* ThumbnailLoader::readImageFromStream(GInputStream* stream, guint64 len, pbuffer += readSize; } QImage image; - image.loadFromData(buffer, totalReadSize); - delete []buffer; + image.loadFromData(buffer.data(), totalReadSize); return image.isNull() ? NULL : fm_qimage_wrapper_new(image); } diff --git a/src/translations/libfm-qt_cs_CZ.ts b/src/translations/libfm-qt_cs_CZ.ts index 582602d..ec2abd7 100644 --- a/src/translations/libfm-qt_cs_CZ.ts +++ b/src/translations/libfm-qt_cs_CZ.ts @@ -58,7 +58,7 @@ Set selected application as default action of this file type - Použít aplikaci jako výchozí pro tento typ souboru + Použít aplikaci jako výchozí pro soubory tohoto typu @@ -271,17 +271,17 @@ Sticky - + SetUID - + SetGID - + @@ -294,7 +294,7 @@ Customize - + Vlastní @@ -302,7 +302,7 @@ Select an application to open "%1" files - + Vyber program, ve kterém se budou otvírat soubory “%1“ @@ -310,12 +310,12 @@ Folder - Adresář + Adresář Blank File - Prázdný soubor + Prázdný soubor @@ -336,17 +336,17 @@ Open in New T&ab - + Otevřít v novém &panelu Open in New Win&dow - + Otevřít v novém &okně Open in Termina&l - + Otevřít v &terminálu @@ -592,32 +592,32 @@ Chceš je odstranit trvale? View folder content - + Zobrazit obsah složky View and modify folder content - + Zobrazit a měnit obsah složky Read - Čtení + Čtení Read and write - + Čtení a zápis Forbidden - + Zakázáno Files of different types - + Soubory různých typů @@ -627,7 +627,7 @@ Chceš je odstranit trvale? Apply changes - + Použít změny @@ -640,17 +640,17 @@ Chceš je odstranit trvale? Error - Chyba + Chyba You should add at least add one directory to search. - + Přidej aspoň jeden adresář k prohledání. Select a folder - + Vybrat adresář @@ -790,7 +790,7 @@ Chceš je odstranit trvale? &Connect - &Připojit + &Připojit @@ -859,27 +859,27 @@ Chceš je odstranit trvale? Open in New Window - Otevřít v novém okně + Otevřít v novém okně Move Bookmark Up - Přesunout záložku nahoru + Přesunout záložku nahoru Move Bookmark Down - Přesunout záložku dolů + Přesunout záložku dolů Rename Bookmark - Přejmenovat záložku + Přejmenovat záložku Remove Bookmark - Odstranit záložku + Odstranit záložku @@ -906,29 +906,33 @@ Chceš je odstranit trvale? Type: %1 Size: %2 Modified: %3 - + Typ: %1 +Velikost: %2 +Změněno: %3 Type: %1 Modified: %2 - + Typ: %1 +Změněno: %2 Type: %1 Modified: %3 - + Typ: %1 +Změněno: %3 &Overwrite - &Přepsat + &Přepsat &Rename - Pře&jmenovat + Pře&jmenovat @@ -948,12 +952,12 @@ Modified: %3 Shows list of common places, devices, and bookmarks in sidebar - Zobrazit seznam obvyklých míst, zařízení a záložek v postranní liště + Zobrazit seznam obvyklých míst, zařízení a záložek v postranní liště Shows tree of directories in sidebar - Zobrazit strom adresářů v postranní liště + Zobrazit strom adresářů v postranní liště @@ -971,37 +975,37 @@ Modified: %3 Connect as u&ser: - Připojit jako &uživatel: + Připojit jako &uživatel: &Username: - Uživatelské &jméno: + &Jméno: &Password: - &Heslo: + &Heslo: &Domain: - &Doména: + &Doména: Forget password &immediately - &Zapomenout heslo + &Zapomenout heslo Remember password until you &logout - Pamatovat si heslo do &odhlášení + Pamatovat si heslo do &odhlášení Remember &forever - Pamatovat si heslo &trvale + Pamatovat si heslo &trvale @@ -1113,176 +1117,176 @@ Modified: %3 Search Files - + Hledat soubory Name/Location - + Jméno/umístění File Name Patterns: - + Jméno souboru obsahuje: * - + Case insensitive - + Nezohledňovat velikost písmen Use regular expression - + Použít regulární výrazy Places to Search: - + Místa k prohledání: &Add - + &Přidat &Remove - + &Odstranit Search in sub directories - + Hledat v podadresářích Search for hidden files - + Hledat skryté soubory File Type - + Typ souboru Only search for files of following types: - + Hledat pouze soubory tohoto typu: Text files - + Textové soubory Image files - + Obrázky Audio files - + Zvuky Video files - + Videa Documents - + Dokumenty Folders - + Adresáře Content - + Obsah File contains: - + Soubor obsahuje: Case insensiti&ve - + Nezohledňovat &velikost písmen &Use regular expression - + Po&užít regulární výrazy Properties - Vlastnosti + Vlastnosti File Size: - + Velikost souboru: Larger than: - + Větší než: Bytes - + Bytů KiB - + MiB - + GiB - + Smaller than: - + Menší než: Last Modified Time: - + Čas poslední změny: Earlier than: - + Dříve než: Later than: - + Později než: diff --git a/src/translations/libfm-qt_de.ts b/src/translations/libfm-qt_de.ts index 6c337fe..5cb146c 100644 --- a/src/translations/libfm-qt_de.ts +++ b/src/translations/libfm-qt_de.ts @@ -641,17 +641,17 @@ Sollen die Dateien stattdessen gelöscht werden? Error - Fehler + Fehler You should add at least add one directory to search. - + Sie sollten mindestens einen Ordner für die Suche hinzufügen. Select a folder - + Einen Ordner wählen @@ -1118,176 +1118,176 @@ Geändert: %3 Search Files - + Dateien suchen Name/Location - + Name/Ort File Name Patterns: - + Muster für Dateinamen: * - + * Case insensitive - + Groß- und Kleinschreibung ignorieren Use regular expression - + Regulären Ausdruck verwenden Places to Search: - + Zu durchsuchende Orte: &Add - + &Hinzufügen &Remove - + &Entfernen Search in sub directories - + In Unterordnern suchen Search for hidden files - + Nach versteckten Dateien suchen File Type - + Dateityp Only search for files of following types: - + Nur nach Dateien der folgenden Typen suchen: Text files - + Textdateien Image files - + Bilddateien Audio files - + Audiodateien Video files - + Videodateien Documents - + Dokumente Folders - + Ordner Content - + Inhalt File contains: - + Datei enthält: Case insensiti&ve - + Groß- und Kleinschreibung ig&norieren &Use regular expression - + Reg&ulären Ausdruck verwenden Properties - Eigenschaften + Eigenschaften File Size: - + Dateigröße: Larger than: - + Größer als: Bytes - + Bytes KiB - + KiB MiB - + MiB GiB - + GiB Smaller than: - + Kleiner als: Last Modified Time: - + Letztes Änderungsdatum: Earlier than: - + Früher als: Later than: - + Später als: diff --git a/src/translations/libfm-qt_lt_LT.ts b/src/translations/libfm-qt_lt_LT.ts index 871fa6e..6e245a7 100644 --- a/src/translations/libfm-qt_lt_LT.ts +++ b/src/translations/libfm-qt_lt_LT.ts @@ -1,6 +1,6 @@ - + AppChooserDialog diff --git a/src/xdndworkaround.cpp b/src/xdndworkaround.cpp new file mode 100644 index 0000000..b7b73a8 --- /dev/null +++ b/src/xdndworkaround.cpp @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2016 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 +#include "xdndworkaround.h" +#include +#include +#include +#include +#include +#include + +// This part is for Qt >= 5.4 only +#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) +#include +#include +#include + +// these are private Qt headers which are not part of Qt APIs +#include // Too bad that we need to use private headers of Qt :-( + +// For some unknown reasons, the event type constants defined in +// xcb/input.h are different from that in X11/extension/XI2.h +// To be safe, we define it ourselves. +#undef XI_ButtonRelease +#define XI_ButtonRelease 5 + +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) + +XdndWorkaround::XdndWorkaround() { + if(!QX11Info::isPlatformX11()) + return; + + // we need to filter all X11 events + qApp->installNativeEventFilter(this); + +// This part is for Qt >= 5.4 only +#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) + lastDrag_ = nullptr; + + // initialize xinput2 since newer versions of Qt5 uses it. + static char xi_name[] = "XInputExtension"; + xcb_connection_t* conn = QX11Info::connection(); + xcb_query_extension_cookie_t cookie = xcb_query_extension(conn, strlen(xi_name), xi_name); + xcb_generic_error_t* err = nullptr; + xcb_query_extension_reply_t* reply = xcb_query_extension_reply(conn, cookie, &err); + if(err == nullptr) { + xinput2Enabled_ = true; + xinputOpCode_ = reply->major_opcode; + xinputEventBase_ = reply->first_event; + xinputErrorBase_ = reply->first_error; + // qDebug() << "xinput: " << m_xi2Enabled << m_xiOpCode << m_xiEventBase; + } + else { + xinput2Enabled_ = false; + free(err); + } + free(reply); +#endif // (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) +} + +XdndWorkaround::~XdndWorkaround() { + if(!QX11Info::isPlatformX11()) + return; + qApp->removeNativeEventFilter(this); +} + +bool XdndWorkaround::nativeEventFilter(const QByteArray & eventType, void * message, long * result) { + if(Q_LIKELY(eventType == "xcb_generic_event_t")) { + xcb_generic_event_t* event = static_cast(message); + uint8_t response_type = event->response_type & uint8_t(~0x80); + switch(event->response_type & ~0x80) { + case XCB_CLIENT_MESSAGE: + return clientMessage(reinterpret_cast(event)); + case XCB_SELECTION_NOTIFY: + return selectionNotify(reinterpret_cast(event)); +// This part is for Qt >= 5.4 only +#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) + case XCB_SELECTION_REQUEST: + return selectionRequest(reinterpret_cast(event)); + case XCB_GE_GENERIC: + // newer versions of Qt5 supports xinput2, which sends its mouse events via XGE. + return genericEvent(reinterpret_cast(event)); + case XCB_BUTTON_RELEASE: + // older versions of Qt5 receive mouse events via old XCB events. + buttonRelease(); + break; +#endif // Qt >= 5.4 + default: + break; + } + } + return false; +} + +// static +QByteArray XdndWorkaround::atomName(xcb_atom_t atom) { + QByteArray name; + xcb_connection_t* conn = QX11Info::connection(); + xcb_get_atom_name_cookie_t cookie = xcb_get_atom_name(conn, atom); + xcb_get_atom_name_reply_t* reply = xcb_get_atom_name_reply(conn, cookie, NULL); + int len = xcb_get_atom_name_name_length(reply); + if(len > 0) { + name.append(xcb_get_atom_name_name(reply), len); + } + free(reply); + return name; +} + +// static +xcb_atom_t XdndWorkaround::internAtom(const char* name, int len) { + xcb_atom_t atom = 0; + if(len == -1) + len = strlen(name); + xcb_connection_t* conn = QX11Info::connection(); + xcb_intern_atom_cookie_t cookie = xcb_intern_atom(conn, false, len, name); + xcb_generic_error_t* err = nullptr; + xcb_intern_atom_reply_t* reply = xcb_intern_atom_reply(conn, cookie, &err); + if(reply != nullptr) { + atom = reply->atom; + free(reply); + } + if(err != nullptr) + free(err); + return atom; +} + +// static +QByteArray XdndWorkaround::windowProperty(xcb_window_t window, xcb_atom_t propAtom, xcb_atom_t typeAtom, int len) { + QByteArray data; + xcb_connection_t* conn = QX11Info::connection(); + xcb_get_property_cookie_t cookie = xcb_get_property(conn, false, window, propAtom, typeAtom, 0, len); + xcb_generic_error_t* err = nullptr; + xcb_get_property_reply_t* reply = xcb_get_property_reply(conn, cookie, &err); + if(reply != nullptr) { + len = xcb_get_property_value_length(reply); + const char* buf = (const char*)xcb_get_property_value(reply); + data.append(buf, len); + free(reply); + } + if(err != nullptr) { + free(err); + } + return data; +} + +// static +void XdndWorkaround::setWindowProperty(xcb_window_t window, xcb_atom_t propAtom, xcb_atom_t typeAtom, void* data, int len, int format) { + xcb_connection_t* conn = QX11Info::connection(); + xcb_void_cookie_t cookie = xcb_change_property(conn, XCB_PROP_MODE_REPLACE, window, propAtom, typeAtom, format, len, data); +} + + +bool XdndWorkaround::clientMessage(xcb_client_message_event_t* event) { + xcb_connection_t* conn = QX11Info::connection(); + QByteArray event_type = atomName(event->type); + // qDebug() << "client message:" << event_type; + + // NOTE: Because of the limitation of Qt, this hack is required to provide + // Xdnd direct save (XDS) protocol support. + // http://www.freedesktop.org/wiki/Specifications/XDS/#index4h2 + // + // XDS requires that the drop target should get and set the window property of the + // drag source to pass the file path, but in Qt there is NO way to know the + // window ID of the drag source so it's not possible to implement XDS with Qt alone. + // Here is a simple hack. We get the drag source window ID with raw XCB code. + // Then, save it on the drop target widget using QObject dynamic property. + // So in the drop event handler of the target widget, it can obtain the + // window ID of the drag source with QObject::property(). + // This hack works 99.99% of the time, but it's not bullet-proof. + // In theory, there is one corner case for which this will not work. + // That is, when you drag multiple XDS sources at the same time and drop + // all of them on the same widget. (Does XDND support doing this?) + // I do not think that any app at the moment support this. + // Even if somebody is using it, X11 will die and we should solve this in Wayland instead. + // + if(event_type == "XdndDrop") { + // data.l[0] contains the XID of the source window. + // data.l[1] is reserved for future use (flags). + // data.l[2] contains the time stamp for retrieving the data. (new in version 1) + QWidget* target = QWidget::find(event->window); + if(target != nullptr) { // drop on our widget + target = qApp->widgetAt(QCursor::pos()); // get the exact child widget that receives the drop + if(target != nullptr) { + target->setProperty("xdnd::lastDragSource", event->data.data32[0]); + target->setProperty("xdnd::lastDropTime", event->data.data32[2]); + } + } + } + // This part is for Qt >= 5.4 only + #if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) + else if(event_type == "XdndFinished") { + lastDrag_ = nullptr; + } + #endif // Qt >= 5.4 + return false; +} + +bool XdndWorkaround::selectionNotify(xcb_selection_notify_event_t* event) { + qDebug() << "selection notify" << atomName(event->selection); + return false; +} + +// This part is for Qt >= 5.4 only +#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) + +bool XdndWorkaround::selectionRequest(xcb_selection_request_event_t* event) { + xcb_connection_t* conn = QX11Info::connection(); + if(event->property == XCB_ATOM_PRIMARY || event->property == XCB_ATOM_SECONDARY) + return false; // we only touch selection requests related to XDnd + QByteArray prop_name = atomName(event->property); + if(prop_name == "CLIPBOARD") + return false; // we do not touch clipboard, either + + xcb_atom_t atomFormat = event->target; + QByteArray type_name = atomName(atomFormat); + // qDebug() << "selection request" << prop_name << type_name; + // We only want to handle text/x-moz-url and text/uri-list + if(type_name == "text/x-moz-url" || type_name.startsWith("text/uri-list")) { + QDragManager* mgr = QDragManager::self(); + QDrag* drag = mgr->object(); + if(drag == nullptr) + drag = lastDrag_; + QMimeData* mime = drag ? drag->mimeData() : nullptr; + if(mime != nullptr && mime->hasUrls()) { + QByteArray data; + QList uris = mime->urls(); + if(type_name == "text/x-moz-url") { + QString mozurl = uris.at(0).toString(QUrl::FullyEncoded); + data.append((const char*)mozurl.utf16(), mozurl.length() * 2); + } + else { // text/uri-list + for(const QUrl& uri : uris) { + data.append(uri.toString(QUrl::FullyEncoded)); + data.append("\r\n"); + } + } + xcb_change_property(conn, XCB_PROP_MODE_REPLACE, event->requestor, event->property, + atomFormat, 8, data.size(), (const void*)data.constData()); + xcb_selection_notify_event_t notify; + notify.response_type = XCB_SELECTION_NOTIFY; + notify.requestor = event->requestor; + notify.selection = event->selection; + notify.time = event->time; + notify.property = event->property; + notify.target = atomFormat; + xcb_window_t proxy_target = event->requestor; + xcb_send_event(conn, false, proxy_target, XCB_EVENT_MASK_NO_EVENT, (const char *)¬ify); + return true; // stop Qt 5 from touching the event + } + } + return false; // let Qt handle this +} + +bool XdndWorkaround::genericEvent(xcb_ge_generic_event_t* event) { + // check this is an xinput event + if(xinput2Enabled_ && event->extension == xinputOpCode_) { + if(event->event_type == XI_ButtonRelease) + buttonRelease(); + } + return false; +} + +void XdndWorkaround::buttonRelease() { + QDragManager* mgr = QDragManager::self(); + lastDrag_ = mgr->object(); + // qDebug() << "BUTTON RELEASE!!!!" << xcbDrag()->canDrop() << lastDrag_; +} + +#endif // QT_VERSION >= QT_VERSION_CHECK(5, 4, 0) diff --git a/src/xdndworkaround.h b/src/xdndworkaround.h new file mode 100644 index 0000000..1a05d79 --- /dev/null +++ b/src/xdndworkaround.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2016 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 + * + */ + +/* + * Note: + * This is a workaround for the following Qt5 bugs. + * + * #49947: Drop events have broken mimeData()->urls() and text/uri-list. + * #47981: Qt5.4 regression: Dropping text/urilist over browser windows stop working. + * + * Related LXQt bug: https://github.com/lxde/lxqt/issues/688 + * + * This workaround is not 100% reliable, but it should work most of the time. + * In theory, when there are multiple drag and drops at nearly the same time and + * you are using a remote X11 instance via a slow network connection, this workaround + * might break. However, that should be a really rare corner case. + * + * How this fix works: + * 1. Hook QApplication to filter raw X11 events + * 2. Intercept SelectionRequest events sent from XDnd target window. + * 3. Check if the data requested have the type "text/uri-list" or "x-moz-url" + * 4. Bypass the broken Qt5 code and send the mime data to the target with our own code. + * + * The mime data is obtained during the most recent mouse button release event. + * This can be incorrect in some corner cases, but it is still a simple and + * good enough approximation that returns the correct data most of the time. + * Anyway, a workarond is just a workaround. Ask Qt developers to fix their bugs. + */ + +#ifndef XDNDWORKAROUND_H +#define XDNDWORKAROUND_H + +#include + +#include +#include +#include +#include + +class QDrag; + +class XdndWorkaround : public QAbstractNativeEventFilter +{ +public: + XdndWorkaround(); + ~XdndWorkaround(); + bool nativeEventFilter(const QByteArray & eventType, void * message, long * result) override; + static QByteArray atomName(xcb_atom_t atom); + static xcb_atom_t internAtom(const char* name, int len = -1); + static QByteArray windowProperty(xcb_window_t window, xcb_atom_t propAtom, xcb_atom_t typeAtom, int len); + static void setWindowProperty(xcb_window_t window, xcb_atom_t propAtom, xcb_atom_t typeAtom, void* data, int len, int format = 8); + +private: + bool clientMessage(xcb_client_message_event_t* event); + bool selectionNotify(xcb_selection_notify_event_t* event); + +// This part is for Qt >= 5.4 only +#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0)) +private: + bool selectionRequest(xcb_selection_request_event_t* event); + bool genericEvent(xcb_ge_generic_event_t *event); + // _QBasicDrag* xcbDrag() const; + void buttonRelease(); + + QDrag* lastDrag_; + // xinput related + bool xinput2Enabled_; + int xinputOpCode_; + int xinputEventBase_; + int xinputErrorBase_; +#endif // Qt >= 5.4 +}; + +#endif // XDNDWORKAROUND_H From 25794f3459c4d3ab5b6a10bc2a18891938c5c392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ChangZhuo=20Chen=20=28=E9=99=B3=E6=98=8C=E5=80=AC=29?= Date: Sat, 19 Mar 2016 14:04:49 +0800 Subject: [PATCH 2/2] Imported Upstream version 0.11.0 --- .gitignore | 9 +++ CMakeLists.txt | 8 +-- src/customaction_p.h | 49 +++++++++++++++ src/filemenu.cpp | 6 ++ src/filemenu_p.h | 29 --------- src/filesearch.ui | 125 ++++++++++++++++++++------------------- src/filesearchdialog.cpp | 2 + src/foldermenu.cpp | 67 +++++++++++++++++++++ src/foldermenu.h | 11 ++++ src/folderview.cpp | 3 + 10 files changed, 215 insertions(+), 94 deletions(-) create mode 100644 .gitignore create mode 100644 src/customaction_p.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf867f3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +build*/ + +# Vim.gitignore +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] +*.un~ +Session.vim +.netrwhist +*~ diff --git a/CMakeLists.txt b/CMakeLists.txt index 2241ded..d90c5e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ project(libfm-qt) set(LIBFM_QT_LIBRARY_NAME "fm-qt" CACHE STRING "fm-qt") set(LIBFM_QT_VERSION_MAJOR 0) -set(LIBFM_QT_VERSION_MINOR 10) +set(LIBFM_QT_VERSION_MINOR 11) set(LIBFM_QT_VERSION_PATCH 0) set(LIBFM_QT_VERSION ${LIBFM_QT_VERSION_MAJOR}.${LIBFM_QT_VERSION_MINOR}.${LIBFM_QT_VERSION_PATCH}) @@ -17,9 +17,9 @@ list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") # Actually, libtool uses different ways on different operating systems. So there is no # universal way to translate a libtool version-info to a cmake version. # We use "(current-age).age.revision" as the cmake version. -# current: 2, revision: 0, age: 0 => version: 2.0.0 -set(LIBFM_QT_LIB_VERSION "2.0.0") -set(LIBFM_QT_LIB_SOVERSION "2") +# current: 3, revision: 0, age: 0 => version: 3.0.0 +set(LIBFM_QT_LIB_VERSION "3.0.0") +set(LIBFM_QT_LIB_SOVERSION "3") set(REQUIRED_QT_VERSION "5.2") set(REQUIRED_LIBFM_VERSION "1.2.0") diff --git a/src/customaction_p.h b/src/customaction_p.h new file mode 100644 index 0000000..e3751f1 --- /dev/null +++ b/src/customaction_p.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2012 - 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_CUSTOMACTION_P_H +#define FM_CUSTOMACTION_P_H + +namespace Fm { + +class CustomAction : public QAction { +public: + explicit CustomAction(FmFileActionItem* item, QObject* parent = NULL): + QAction(QString::fromUtf8(fm_file_action_item_get_name(item)), parent), + item_(reinterpret_cast(fm_file_action_item_ref(item))) { + const char* icon_name = fm_file_action_item_get_icon(item); + if(icon_name) + setIcon(QIcon::fromTheme(icon_name)); + } + + virtual ~CustomAction() { + fm_file_action_item_unref(item_); + } + + FmFileActionItem* item() { + return item_; + } + +private: + FmFileActionItem* item_; +}; + +} // namespace Fm + +#endif diff --git a/src/filemenu.cpp b/src/filemenu.cpp index f5692bd..2a1ff8d 100644 --- a/src/filemenu.cpp +++ b/src/filemenu.cpp @@ -28,6 +28,7 @@ #include "appchooserdialog.h" #ifdef CUSTOM_ACTIONS #include +#include "customaction_p.h" #endif #include #include @@ -184,6 +185,11 @@ void FileMenu::createMenu(FmFileInfoList* files, FmFileInfo* info, FmPath* cwd) GList* l; for(l=items; l; l=l->next) { FmFileActionItem* item = FM_FILE_ACTION_ITEM(l->data); + if(l == items && item + && !(fm_file_action_item_is_action(item) + && !(fm_file_action_item_get_target(item) & FM_FILE_ACTION_TARGET_CONTEXT))) { + addSeparator(); // before all custom actions + } addCustomActionItem(this, item); } } diff --git a/src/filemenu_p.h b/src/filemenu_p.h index 94376d8..73a6831 100644 --- a/src/filemenu_p.h +++ b/src/filemenu_p.h @@ -21,9 +21,6 @@ #define FM_FILEMENU_P_H #include "icontheme.h" -#ifdef CUSTOM_ACTIONS -#include -#endif #include namespace Fm { @@ -53,32 +50,6 @@ private: GAppInfo* appInfo_; }; -#ifdef CUSTOM_ACTIONS -class CustomAction : public QAction { - Q_OBJECT -public: - explicit CustomAction(FmFileActionItem* item, QObject* parent = NULL): - QAction(QString::fromUtf8(fm_file_action_item_get_name(item)), parent), - item_(reinterpret_cast(fm_file_action_item_ref(item))) { - const char* icon_name = fm_file_action_item_get_icon(item); - if(icon_name) - setIcon(QIcon::fromTheme(icon_name)); - } - - virtual ~CustomAction() { - fm_file_action_item_unref(item_); - } - - FmFileActionItem* item() { - return item_; - } - -private: - FmFileActionItem* item_; -}; - -#endif - } // namespace Fm #endif diff --git a/src/filesearch.ui b/src/filesearch.ui index 1cb466f..2919edb 100644 --- a/src/filesearch.ui +++ b/src/filesearch.ui @@ -261,22 +261,36 @@ - - - Larger than: - - - - - - + + + + + Smaller than: + + + + + + + false + + + + false - + + + + Larger than: + + + + false @@ -306,25 +320,7 @@ - - - - - - Smaller than: - - - - - - - - - false - - - - + false @@ -361,43 +357,50 @@ + + Qt::LeftToRight + Last Modified Time: - - - - Earlier than: - - - - - - Later than: - - - - - - - false - - - true - - - - - - - false - - - true - - + + + + + Earlier than: + + + + + + + Later than: + + + + + + + false + + + true + + + + + + + false + + + true + + + + diff --git a/src/filesearchdialog.cpp b/src/filesearchdialog.cpp index 022747e..4fc8699 100644 --- a/src/filesearchdialog.cpp +++ b/src/filesearchdialog.cpp @@ -41,6 +41,8 @@ FileSearchDialog::FileSearchDialog(QStringList paths, QWidget* parent, Qt::Windo connect(ui->addPath, &QPushButton::clicked, this, &FileSearchDialog::onAddPath); connect(ui->removePath, &QPushButton::clicked, this, &FileSearchDialog::onRemovePath); + + ui->namePatterns->setFocus(); } FileSearchDialog::~FileSearchDialog() { diff --git a/src/foldermenu.cpp b/src/foldermenu.cpp index bd1bb4a..fb74883 100644 --- a/src/foldermenu.cpp +++ b/src/foldermenu.cpp @@ -25,6 +25,10 @@ #include "folderview.h" #include "utilities.h" #include // for memset +#ifdef CUSTOM_ACTIONS +#include "customaction_p.h" +#include +#endif namespace Fm { @@ -68,6 +72,29 @@ FolderMenu::FolderMenu(FolderView* view, QWidget* parent): showHiddenAction_->setChecked(model->showHidden()); connect(showHiddenAction_, &QAction::triggered, this, &FolderMenu::onShowHiddenActionTriggered); +#ifdef CUSTOM_ACTIONS + FmFileInfo* folderInfo = view_->folderInfo(); + if(folderInfo) { + GList *single_list = NULL; + single_list = g_list_prepend(single_list, (GList*)folderInfo); + GList* items = fm_get_actions_for_files(single_list); + if(items) { + GList* l; + for(l=items; l; l=l->next) { + FmFileActionItem* item = FM_FILE_ACTION_ITEM(l->data); + if(l == items && item + && !(fm_file_action_item_is_action(item) + && !(fm_file_action_item_get_target(item) & FM_FILE_ACTION_TARGET_CONTEXT))) { + addSeparator(); // before all custom actions + } + addCustomActionItem(this, item); + } + } + g_list_foreach(items, (GFunc)fm_file_action_item_unref, NULL); + g_list_free(items); + } +#endif + separator4_ = addSeparator(); propertiesAction_ = new QAction(tr("Folder Pr&operties"), this); @@ -78,6 +105,46 @@ FolderMenu::FolderMenu(FolderView* view, QWidget* parent): FolderMenu::~FolderMenu() { } +#ifdef CUSTOM_ACTIONS +void FolderMenu::addCustomActionItem(QMenu* menu, FmFileActionItem* item) { + if(!item) return; + if(fm_file_action_item_is_action(item) && !(fm_file_action_item_get_target(item) & FM_FILE_ACTION_TARGET_CONTEXT)) + return; + + CustomAction* action = new CustomAction(item, menu); + menu->addAction(action); + if(fm_file_action_item_is_menu(item)) { + GList* subitems = fm_file_action_item_get_sub_items(item); + for(GList* l = subitems; l; l = l->next) { + FmFileActionItem* subitem = FM_FILE_ACTION_ITEM(l->data); + QMenu* submenu = new QMenu(menu); + addCustomActionItem(submenu, subitem); + action->setMenu(submenu); + } + } + else if(fm_file_action_item_is_action(item)) { + connect(action, &QAction::triggered, this, &FolderMenu::onCustomActionTrigerred); + } +} + +void FolderMenu::onCustomActionTrigerred() { + CustomAction* action = static_cast(sender()); + FmFileActionItem* item = action->item(); + + FmFileInfo* folderInfo = view_->folderInfo(); + if(folderInfo) { + GList *single_list = NULL; + single_list = g_list_prepend(single_list, (GList*)folderInfo); + char* output = NULL; + fm_file_action_item_launch(item, NULL, single_list, &output); + if(output) { + QMessageBox::information(this, tr("Output"), QString::fromUtf8(output)); + g_free(output); + } + } +} +#endif + void FolderMenu::addSortMenuItem(QString title, int id) { QAction* action = new QAction(title, this); sortMenu_->addAction(action); diff --git a/src/foldermenu.h b/src/foldermenu.h index fd2cbb4..8308153 100644 --- a/src/foldermenu.h +++ b/src/foldermenu.h @@ -25,6 +25,9 @@ #include #include #include "foldermodel.h" +#ifdef CUSTOM_ACTIONS +#include +#endif class QAction; @@ -87,6 +90,11 @@ public: return view_; } +protected: +#ifdef CUSTOM_ACTIONS + void addCustomActionItem(QMenu* menu, FmFileActionItem* item); +#endif + protected Q_SLOTS: void onPasteActionTriggered(); void onSelectAllActionTriggered(); @@ -97,6 +105,9 @@ protected Q_SLOTS: void onCaseSensitiveActionTriggered(bool checked); void onFolderFirstActionTriggered(bool checked); void onPropertiesActionTriggered(); +#ifdef CUSTOM_ACTIONS + void onCustomActionTrigerred(); +#endif private: void createSortMenu(); diff --git a/src/folderview.cpp b/src/folderview.cpp index 6814e49..e63a53f 100644 --- a/src/folderview.cpp +++ b/src/folderview.cpp @@ -473,6 +473,7 @@ void FolderView::setViewMode(ViewMode _mode) { if(mode == DetailedListMode) { FolderViewTreeView* treeView = new FolderViewTreeView(this); connect(treeView, &FolderViewTreeView::activatedFiltered, this, &FolderView::onItemActivated); + setFocusProxy(treeView); view = treeView; treeView->setItemsExpandable(false); @@ -492,6 +493,8 @@ void FolderView::setViewMode(ViewMode _mode) { connect(listView, &FolderViewListView::activatedFiltered, this, &FolderView::onItemActivated); view = listView; } + setFocusProxy(listView); + // set our own custom delegate FolderItemDelegate* delegate = new FolderItemDelegate(listView); listView->setItemDelegateForColumn(FolderModel::ColumnFileName, delegate);