Manual merge the new release 0.11.0 from debian/experimental

ubuntu/cosmic
Alf Gaida 8 years ago
commit 108dbfc235

9
.gitignore vendored

@ -0,0 +1,9 @@
build*/
# Vim.gitignore
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
*.un~
Session.vim
.netrwhist
*~

@ -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")
@ -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}")

7
debian/changelog vendored

@ -1,3 +1,10 @@
libfm-qt (0.11.0-1) experimental; urgency=medium
* New upstream release.
* Bump soversion to 3.
-- ChangZhuo Chen (陳昌倬) <czchen@debian.org> Sat, 19 Mar 2016 15:51:15 +0800
libfm-qt (0.10.0+20151214-3) unstable; urgency=medium
* Add hardening=+all

4
debian/copyright vendored

@ -17,6 +17,10 @@ Files: cmake/Qt5PatchedLinguistToolsMacros.cmake
Copyright: 2005-2011 Kitware, Inc.
License: BSD-3-Clause
Files: src/xdndworkaround.*
Copyright: 2016 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
License: LGPL-2.1+
Files: debian/*
Copyright: 2014-2015 Wen Liao <wen.cf83@gmail.com>
2014-2016 ChangZhuo Chen (陳昌倬) <czchen@debian.org>

4
debian/gbp.conf vendored

@ -2,3 +2,7 @@
debian-branch = debian/sid
upstream-branch = upstream/latest
pristine-tar = True
[import-orig]
# Use git cherrypick -n upstream instead.
merge = False

@ -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 "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)

@ -0,0 +1,49 @@
/*
* Copyright (C) 2012 - 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_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<FmFileActionItem*>(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

@ -28,6 +28,7 @@
#include "appchooserdialog.h"
#ifdef CUSTOM_ACTIONS
#include <libfm/fm-actions.h>
#include "customaction_p.h"
#endif
#include <QMessageBox>
#include <QDebug>
@ -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);
}
}

@ -21,9 +21,6 @@
#define FM_FILEMENU_P_H
#include "icontheme.h"
#ifdef CUSTOM_ACTIONS
#include <libfm/fm-actions.h>
#endif
#include <QDebug>
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<FmFileActionItem*>(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

@ -261,19 +261,40 @@
</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"/>
<layout class="QGridLayout" name="gridLayout_2">
<item row="1" column="0">
<widget class="QCheckBox" name="smallerThan">
<property name="text">
<string>Smaller than:</string>
</property>
</widget>
</item>
<item>
<item row="1" column="2">
<widget class="QSpinBox" name="maxSize">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QSpinBox" name="minSize">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="largerThan">
<property name="text">
<string>Larger than:</string>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QComboBox" name="minSizeUnit">
<property name="enabled">
<bool>false</bool>
</property>
<property name="currentIndex">
<number>2</number>
</property>
@ -299,22 +320,11 @@
</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>
<item row="1" column="3">
<widget class="QComboBox" name="maxSizeUnit">
<property name="enabled">
<bool>false</bool>
</property>
<property name="currentIndex">
<number>2</number>
</property>
@ -347,37 +357,50 @@
</item>
<item>
<widget class="QGroupBox" name="groupBox_6">
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<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>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QCheckBox" name="earlierThan">
<property name="text">
<string>Earlier than:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QCheckBox" name="laterThan">
<property name="text">
<string>Later than:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDateEdit" name="minTime">
<property name="enabled">
<bool>false</bool>
</property>
<property name="calendarPopup">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDateEdit" name="maxTime">
<property name="enabled">
<bool>false</bool>
</property>
<property name="calendarPopup">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
@ -445,5 +468,101 @@
</hint>
</hints>
</connection>
<connection>
<sender>largerThan</sender>
<signal>toggled(bool)</signal>
<receiver>minSizeUnit</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>93</x>
<y>84</y>
</hint>
<hint type="destinationlabel">
<x>403</x>
<y>88</y>
</hint>
</hints>
</connection>
<connection>
<sender>smallerThan</sender>
<signal>toggled(bool)</signal>
<receiver>maxSize</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>96</x>
<y>119</y>
</hint>
<hint type="destinationlabel">
<x>241</x>
<y>123</y>
</hint>
</hints>
</connection>
<connection>
<sender>largerThan</sender>
<signal>toggled(bool)</signal>
<receiver>minSize</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>93</x>
<y>84</y>
</hint>
<hint type="destinationlabel">
<x>241</x>
<y>88</y>
</hint>
</hints>
</connection>
<connection>
<sender>smallerThan</sender>
<signal>toggled(bool)</signal>
<receiver>maxSizeUnit</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>96</x>
<y>119</y>
</hint>
<hint type="destinationlabel">
<x>403</x>
<y>123</y>
</hint>
</hints>
</connection>
<connection>
<sender>laterThan</sender>
<signal>toggled(bool)</signal>
<receiver>minTime</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>88</x>
<y>223</y>
</hint>
<hint type="destinationlabel">
<x>319</x>
<y>226</y>
</hint>
</hints>
</connection>
<connection>
<sender>earlierThan</sender>
<signal>toggled(bool)</signal>
<receiver>maxTime</receiver>
<slot>setEnabled(bool)</slot>
<hints>
<hint type="sourcelabel">
<x>93</x>
<y>190</y>
</hint>
<hint type="destinationlabel">
<x>319</x>
<y>193</y>
</hint>
</hints>
</connection>
</connections>
</ui>

@ -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() {
@ -101,7 +103,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)

@ -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);

@ -25,6 +25,10 @@
#include "folderview.h"
#include "utilities.h"
#include <cstring> // for memset
#ifdef CUSTOM_ACTIONS
#include "customaction_p.h"
#include <QMessageBox>
#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<CustomAction*>(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);

@ -25,6 +25,9 @@
#include <QMenu>
#include <libfm/fm.h>
#include "foldermodel.h"
#ifdef CUSTOM_ACTIONS
#include <libfm/fm-actions.h>
#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();

@ -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;

@ -37,6 +37,10 @@
#include <QApplication>
#include <QScrollBar>
#include <QMetaType>
#include <QX11Info> // for XDS support
#include <xcb/xcb.h> // 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);
@ -468,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);
@ -487,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);
@ -577,7 +585,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<FolderItemDelegate*>(listView->itemDelegateForColumn(FolderModel::ColumnFileName));
@ -600,6 +608,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 +815,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.

@ -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_;
};
}

@ -22,6 +22,7 @@
#include <QLocale>
#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)
};

@ -25,6 +25,7 @@
#include <QStringBuilder>
#include <QThread>
#include <QDebug>
#include <QKeyEvent>
#include <libfm/fm.h>
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<QKeyEvent*>(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

@ -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();

@ -21,6 +21,7 @@
#include "thumbnailloader.h"
#include <new>
#include <QByteArray>
#include <QScopedArrayPointer>
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<unsigned char> 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);
}

@ -58,7 +58,7 @@
<message>
<location filename="../app-chooser-dialog.ui" line="109"/>
<source>Set selected application as default action of this file type</source>
<translation>Použít aplikaci jako výchozí pro tento typ souboru</translation>
<translation>Použít aplikaci jako výchozí pro soubory tohoto typu</translation>
</message>
</context>
<context>
@ -271,17 +271,17 @@
<message>
<location filename="../file-props.ui" line="603"/>
<source>Sticky</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../file-props.ui" line="610"/>
<source>SetUID</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../file-props.ui" line="617"/>
<source>SetGID</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../file-props.ui" line="653"/>
@ -294,7 +294,7 @@
<message>
<location filename="../appchoosercombobox.cpp" line="79"/>
<source>Customize</source>
<translation type="unfinished"></translation>
<translation>Vlastní</translation>
</message>
</context>
<context>
@ -302,7 +302,7 @@
<message>
<location filename="../appchooserdialog.cpp" line="262"/>
<source>Select an application to open &quot;%1&quot; files</source>
<translation type="unfinished"></translation>
<translation>Vyber program, ve kterém se budou otvírat soubory %1</translation>
</message>
</context>
<context>
@ -310,12 +310,12 @@
<message>
<location filename="../createnewmenu.cpp" line="29"/>
<source>Folder</source>
<translation type="unfinished">Adresář</translation>
<translation>Adresář</translation>
</message>
<message>
<location filename="../createnewmenu.cpp" line="33"/>
<source>Blank File</source>
<translation type="unfinished">Prázdný soubor</translation>
<translation>Prázdný soubor</translation>
</message>
</context>
<context>
@ -336,17 +336,17 @@
<message>
<location filename="../dirtreeview.cpp" line="208"/>
<source>Open in New T&amp;ab</source>
<translation type="unfinished"></translation>
<translation>Otevřít v novém &amp;panelu</translation>
</message>
<message>
<location filename="../dirtreeview.cpp" line="212"/>
<source>Open in New Win&amp;dow</source>
<translation type="unfinished"></translation>
<translation>Otevřít v novém &amp;okně</translation>
</message>
<message>
<location filename="../dirtreeview.cpp" line="217"/>
<source>Open in Termina&amp;l</source>
<translation type="unfinished"></translation>
<translation>Otevřít v &amp;terminálu</translation>
</message>
</context>
<context>
@ -592,32 +592,32 @@ Chceš je odstranit trvale?</translation>
<message>
<location filename="../filepropsdialog.cpp" line="145"/>
<source>View folder content</source>
<translation type="unfinished"></translation>
<translation>Zobrazit obsah složky</translation>
</message>
<message>
<location filename="../filepropsdialog.cpp" line="146"/>
<source>View and modify folder content</source>
<translation type="unfinished"></translation>
<translation>Zobrazit a měnit obsah složky</translation>
</message>
<message>
<location filename="../filepropsdialog.cpp" line="150"/>
<source>Read</source>
<translation type="unfinished">Čtení</translation>
<translation>Čtení</translation>
</message>
<message>
<location filename="../filepropsdialog.cpp" line="151"/>
<source>Read and write</source>
<translation type="unfinished"></translation>
<translation>Čtení a zápis</translation>
</message>
<message>
<location filename="../filepropsdialog.cpp" line="153"/>
<source>Forbidden</source>
<translation type="unfinished"></translation>
<translation>Zakázáno</translation>
</message>
<message>
<location filename="../filepropsdialog.cpp" line="257"/>
<source>Files of different types</source>
<translation type="unfinished"></translation>
<translation>Soubory různých typů</translation>
</message>
<message>
<location filename="../filepropsdialog.cpp" line="287"/>
@ -627,7 +627,7 @@ Chceš je odstranit trvale?</translation>
<message>
<location filename="../filepropsdialog.cpp" line="415"/>
<source>Apply changes</source>
<translation type="unfinished"></translation>
<translation>Použít změny</translation>
</message>
<message>
<location filename="../filepropsdialog.cpp" line="416"/>
@ -640,17 +640,17 @@ Chceš je odstranit trvale?</translation>
<message>
<location filename="../filesearchdialog.cpp" line="120"/>
<source>Error</source>
<translation type="unfinished">Chyba</translation>
<translation>Chyba</translation>
</message>
<message>
<location filename="../filesearchdialog.cpp" line="120"/>
<source>You should add at least add one directory to search.</source>
<translation type="unfinished"></translation>
<translation>Přidej aspoň jeden adresář k prohledání.</translation>
</message>
<message>
<location filename="../filesearchdialog.cpp" line="127"/>
<source>Select a folder</source>
<translation type="unfinished"></translation>
<translation>Vybrat adresář</translation>
</message>
</context>
<context>
@ -790,7 +790,7 @@ Chceš je odstranit trvale?</translation>
<message>
<location filename="../mountoperationpassworddialog.cpp" line="40"/>
<source>&amp;Connect</source>
<translation type="unfinished">&amp;Připojit</translation>
<translation>&amp;Připojit</translation>
</message>
</context>
<context>
@ -859,27 +859,27 @@ Chceš je odstranit trvale?</translation>
<message>
<location filename="../placesview.cpp" line="349"/>
<source>Open in New Window</source>
<translation type="unfinished">Otevřít v novém okně</translation>
<translation>Otevřít v novém okně</translation>
</message>
<message>
<location filename="../placesview.cpp" line="367"/>
<source>Move Bookmark Up</source>
<translation type="unfinished">Přesunout záložku nahoru</translation>
<translation>Přesunout záložku nahoru</translation>
</message>
<message>
<location filename="../placesview.cpp" line="372"/>
<source>Move Bookmark Down</source>
<translation type="unfinished">Přesunout záložku dolů</translation>
<translation>Přesunout záložku dolů</translation>
</message>
<message>
<location filename="../placesview.cpp" line="376"/>
<source>Rename Bookmark</source>
<translation type="unfinished">Přejmenovat záložku</translation>
<translation>Přejmenovat záložku</translation>
</message>
<message>
<location filename="../placesview.cpp" line="379"/>
<source>Remove Bookmark</source>
<translation type="unfinished">Odstranit záložku</translation>
<translation>Odstranit záložku</translation>
</message>
<message>
<location filename="../placesview.cpp" line="388"/>
@ -906,29 +906,33 @@ Chceš je odstranit trvale?</translation>
<source>Type: %1
Size: %2
Modified: %3</source>
<translation type="unfinished"></translation>
<translation>Typ: %1
Velikost: %2
Změněno: %3</translation>
</message>
<message>
<location filename="../renamedialog.cpp" line="56"/>
<source>Type: %1
Modified: %2</source>
<translation type="unfinished"></translation>
<translation>Typ: %1
Změněno: %2</translation>
</message>
<message>
<location filename="../renamedialog.cpp" line="75"/>
<source>Type: %1
Modified: %3</source>
<translation type="unfinished"></translation>
<translation>Typ: %1
Změněno: %3</translation>
</message>
<message>
<location filename="../renamedialog.cpp" line="89"/>
<source>&amp;Overwrite</source>
<translation type="unfinished">&amp;Přepsat</translation>
<translation>&amp;Přepsat</translation>
</message>
<message>
<location filename="../renamedialog.cpp" line="91"/>
<source>&amp;Rename</source>
<translation type="unfinished">Pře&amp;jmenovat</translation>
<translation>Pře&amp;jmenovat</translation>
</message>
</context>
<context>
@ -948,12 +952,12 @@ Modified: %3</source>
<message>
<location filename="../sidepane.cpp" line="137"/>
<source>Shows list of common places, devices, and bookmarks in sidebar</source>
<translation type="unfinished">Zobrazit seznam obvyklých míst, zařízení a záložek v postranní liště</translation>
<translation>Zobrazit seznam obvyklých míst, zařízení a záložek v postranní liště</translation>
</message>
<message>
<location filename="../sidepane.cpp" line="139"/>
<source>Shows tree of directories in sidebar</source>
<translation type="unfinished">Zobrazit strom adresářů v postranní liště</translation>
<translation>Zobrazit strom adresářů v postranní liště</translation>
</message>
</context>
<context>
@ -971,37 +975,37 @@ Modified: %3</source>
<message>
<location filename="../mount-operation-password.ui" line="58"/>
<source>Connect as u&amp;ser:</source>
<translation type="unfinished">Připojit jako &amp;uživatel:</translation>
<translation>Připojit jako &amp;uživatel:</translation>
</message>
<message>
<location filename="../mount-operation-password.ui" line="79"/>
<source>&amp;Username:</source>
<translation type="unfinished">Uživatelské &amp;jméno:</translation>
<translation>&amp;Jméno:</translation>
</message>
<message>
<location filename="../mount-operation-password.ui" line="102"/>
<source>&amp;Password:</source>
<translation type="unfinished">&amp;Heslo:</translation>
<translation>&amp;Heslo:</translation>
</message>
<message>
<location filename="../mount-operation-password.ui" line="112"/>
<source>&amp;Domain:</source>
<translation type="unfinished">&amp;Doména:</translation>
<translation>&amp;Doména:</translation>
</message>
<message>
<location filename="../mount-operation-password.ui" line="127"/>
<source>Forget password &amp;immediately</source>
<translation type="unfinished">&amp;Zapomenout heslo</translation>
<translation>&amp;Zapomenout heslo</translation>
</message>
<message>
<location filename="../mount-operation-password.ui" line="137"/>
<source>Remember password until you &amp;logout</source>
<translation type="unfinished">Pamatovat si heslo do &amp;odhlášení</translation>
<translation>Pamatovat si heslo do &amp;odhlášení</translation>
</message>
<message>
<location filename="../mount-operation-password.ui" line="147"/>
<source>Remember &amp;forever</source>
<translation type="unfinished">Pamatovat si heslo &amp;trvale</translation>
<translation>Pamatovat si heslo &amp;trvale</translation>
</message>
</context>
<context>
@ -1113,176 +1117,176 @@ Modified: %3</source>
<message>
<location filename="../filesearch.ui" line="14"/>
<source>Search Files</source>
<translation type="unfinished"></translation>
<translation>Hledat soubory</translation>
</message>
<message>
<location filename="../filesearch.ui" line="29"/>
<source>Name/Location</source>
<translation type="unfinished"></translation>
<translation>Jméno/umístění</translation>
</message>
<message>
<location filename="../filesearch.ui" line="35"/>
<source>File Name Patterns:</source>
<translation type="unfinished"></translation>
<translation>Jméno souboru obsahuje:</translation>
</message>
<message>
<location filename="../filesearch.ui" line="41"/>
<source>*</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../filesearch.ui" line="48"/>
<source>Case insensitive</source>
<translation type="unfinished"></translation>
<translation>Nezohledňovat velikost písmen</translation>
</message>
<message>
<location filename="../filesearch.ui" line="55"/>
<source>Use regular expression</source>
<translation type="unfinished"></translation>
<translation>Použít regulární výrazy</translation>
</message>
<message>
<location filename="../filesearch.ui" line="65"/>
<source>Places to Search:</source>
<translation type="unfinished"></translation>
<translation>Místa k prohledání:</translation>
</message>
<message>
<location filename="../filesearch.ui" line="78"/>
<source>&amp;Add</source>
<translation type="unfinished"></translation>
<translation>&amp;Přidat</translation>
</message>
<message>
<location filename="../filesearch.ui" line="90"/>
<source>&amp;Remove</source>
<translation type="unfinished"></translation>
<translation>&amp;Odstranit</translation>
</message>
<message>
<location filename="../filesearch.ui" line="119"/>
<source>Search in sub directories</source>
<translation type="unfinished"></translation>
<translation>Hledat v podadresářích</translation>
</message>
<message>
<location filename="../filesearch.ui" line="126"/>
<source>Search for hidden files</source>
<translation type="unfinished"></translation>
<translation>Hledat skryté soubory</translation>
</message>
<message>
<location filename="../filesearch.ui" line="137"/>
<source>File Type</source>
<translation type="unfinished"></translation>
<translation>Typ souboru</translation>
</message>
<message>
<location filename="../filesearch.ui" line="143"/>
<source>Only search for files of following types:</source>
<translation type="unfinished"></translation>
<translation>Hledat pouze soubory tohoto typu:</translation>
</message>
<message>
<location filename="../filesearch.ui" line="149"/>
<source>Text files</source>
<translation type="unfinished"></translation>
<translation>Textové soubory</translation>
</message>
<message>
<location filename="../filesearch.ui" line="156"/>
<source>Image files</source>
<translation type="unfinished"></translation>
<translation>Obrázky</translation>
</message>
<message>
<location filename="../filesearch.ui" line="163"/>
<source>Audio files</source>
<translation type="unfinished"></translation>
<translation>Zvuky</translation>
</message>
<message>
<location filename="../filesearch.ui" line="170"/>
<source>Video files</source>
<translation type="unfinished"></translation>
<translation>Videa</translation>
</message>
<message>
<location filename="../filesearch.ui" line="177"/>
<source>Documents</source>
<translation type="unfinished"></translation>
<translation>Dokumenty</translation>
</message>
<message>
<location filename="../filesearch.ui" line="184"/>
<source>Folders</source>
<translation type="unfinished"></translation>
<translation>Adresáře</translation>
</message>
<message>
<location filename="../filesearch.ui" line="208"/>
<source>Content</source>
<translation type="unfinished"></translation>
<translation>Obsah</translation>
</message>
<message>
<location filename="../filesearch.ui" line="214"/>
<source>File contains:</source>
<translation type="unfinished"></translation>
<translation>Soubor obsahuje:</translation>
</message>
<message>
<location filename="../filesearch.ui" line="223"/>
<source>Case insensiti&amp;ve</source>
<translation type="unfinished"></translation>
<translation>Nezohledňovat &amp;velikost písmen</translation>
</message>
<message>
<location filename="../filesearch.ui" line="230"/>
<source>&amp;Use regular expression</source>
<translation type="unfinished"></translation>
<translation>Po&amp;užít regulární výrazy</translation>
</message>
<message>
<location filename="../filesearch.ui" line="254"/>
<source>Properties</source>
<translation type="unfinished">Vlastnosti</translation>
<translation>Vlastnosti</translation>
</message>
<message>
<location filename="../filesearch.ui" line="260"/>
<source>File Size:</source>
<translation type="unfinished"></translation>
<translation>Velikost souboru:</translation>
</message>
<message>
<location filename="../filesearch.ui" line="266"/>
<source>Larger than:</source>
<translation type="unfinished"></translation>
<translation>Větší než:</translation>
</message>
<message>
<location filename="../filesearch.ui" line="282"/>
<location filename="../filesearch.ui" line="323"/>
<source>Bytes</source>
<translation type="unfinished"></translation>
<translation>Bytů</translation>
</message>
<message>
<location filename="../filesearch.ui" line="287"/>
<location filename="../filesearch.ui" line="328"/>
<source>KiB</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../filesearch.ui" line="292"/>
<location filename="../filesearch.ui" line="333"/>
<source>MiB</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../filesearch.ui" line="297"/>
<location filename="../filesearch.ui" line="338"/>
<source>GiB</source>
<translation type="unfinished"></translation>
<translation></translation>
</message>
<message>
<location filename="../filesearch.ui" line="307"/>
<source>Smaller than:</source>
<translation type="unfinished"></translation>
<translation>Menší než:</translation>
</message>
<message>
<location filename="../filesearch.ui" line="351"/>
<source>Last Modified Time:</source>
<translation type="unfinished"></translation>
<translation>Čas poslední změny:</translation>
</message>
<message>
<location filename="../filesearch.ui" line="357"/>
<source>Earlier than:</source>
<translation type="unfinished"></translation>
<translation>Dříve než:</translation>
</message>
<message>
<location filename="../filesearch.ui" line="364"/>
<source>Later than:</source>
<translation type="unfinished"></translation>
<translation>Později než:</translation>
</message>
</context>
</TS>

@ -641,17 +641,17 @@ Sollen die Dateien stattdessen gelöscht werden?</translation>
<message>
<location filename="../filesearchdialog.cpp" line="120"/>
<source>Error</source>
<translation type="unfinished">Fehler</translation>
<translation>Fehler</translation>
</message>
<message>
<location filename="../filesearchdialog.cpp" line="120"/>
<source>You should add at least add one directory to search.</source>
<translation type="unfinished"></translation>
<translation>Sie sollten mindestens einen Ordner für die Suche hinzufügen.</translation>
</message>
<message>
<location filename="../filesearchdialog.cpp" line="127"/>
<source>Select a folder</source>
<translation type="unfinished"></translation>
<translation>Einen Ordner wählen</translation>
</message>
</context>
<context>
@ -1118,176 +1118,176 @@ Geändert: %3</translation>
<message>
<location filename="../filesearch.ui" line="14"/>
<source>Search Files</source>
<translation type="unfinished"></translation>
<translation>Dateien suchen</translation>
</message>
<message>
<location filename="../filesearch.ui" line="29"/>
<source>Name/Location</source>
<translation type="unfinished"></translation>
<translation>Name/Ort</translation>
</message>
<message>
<location filename="../filesearch.ui" line="35"/>
<source>File Name Patterns:</source>
<translation type="unfinished"></translation>
<translation>Muster für Dateinamen:</translation>
</message>
<message>
<location filename="../filesearch.ui" line="41"/>
<source>*</source>
<translation type="unfinished"></translation>
<translation>*</translation>
</message>
<message>
<location filename="../filesearch.ui" line="48"/>
<source>Case insensitive</source>
<translation type="unfinished"></translation>
<translation>Groß- und Kleinschreibung ignorieren</translation>
</message>
<message>
<location filename="../filesearch.ui" line="55"/>
<source>Use regular expression</source>
<translation type="unfinished"></translation>
<translation>Regulären Ausdruck verwenden</translation>
</message>
<message>
<location filename="../filesearch.ui" line="65"/>
<source>Places to Search:</source>
<translation type="unfinished"></translation>
<translation>Zu durchsuchende Orte:</translation>
</message>
<message>
<location filename="../filesearch.ui" line="78"/>
<source>&amp;Add</source>
<translation type="unfinished"></translation>
<translation>&amp;Hinzufügen</translation>
</message>
<message>
<location filename="../filesearch.ui" line="90"/>
<source>&amp;Remove</source>
<translation type="unfinished"></translation>
<translation>&amp;Entfernen</translation>
</message>
<message>
<location filename="../filesearch.ui" line="119"/>
<source>Search in sub directories</source>
<translation type="unfinished"></translation>
<translation>In Unterordnern suchen</translation>
</message>
<message>
<location filename="../filesearch.ui" line="126"/>
<source>Search for hidden files</source>
<translation type="unfinished"></translation>
<translation>Nach versteckten Dateien suchen</translation>
</message>
<message>
<location filename="../filesearch.ui" line="137"/>
<source>File Type</source>
<translation type="unfinished"></translation>
<translation>Dateityp</translation>
</message>
<message>
<location filename="../filesearch.ui" line="143"/>
<source>Only search for files of following types:</source>
<translation type="unfinished"></translation>
<translation>Nur nach Dateien der folgenden Typen suchen:</translation>
</message>
<message>
<location filename="../filesearch.ui" line="149"/>
<source>Text files</source>
<translation type="unfinished"></translation>
<translation>Textdateien</translation>
</message>
<message>
<location filename="../filesearch.ui" line="156"/>
<source>Image files</source>
<translation type="unfinished"></translation>
<translation>Bilddateien</translation>
</message>
<message>
<location filename="../filesearch.ui" line="163"/>
<source>Audio files</source>
<translation type="unfinished"></translation>
<translation>Audiodateien</translation>
</message>
<message>
<location filename="../filesearch.ui" line="170"/>
<source>Video files</source>
<translation type="unfinished"></translation>
<translation>Videodateien</translation>
</message>
<message>
<location filename="../filesearch.ui" line="177"/>
<source>Documents</source>
<translation type="unfinished"></translation>
<translation>Dokumente</translation>
</message>
<message>
<location filename="../filesearch.ui" line="184"/>
<source>Folders</source>
<translation type="unfinished"></translation>
<translation>Ordner</translation>
</message>
<message>
<location filename="../filesearch.ui" line="208"/>
<source>Content</source>
<translation type="unfinished"></translation>
<translation>Inhalt</translation>
</message>
<message>
<location filename="../filesearch.ui" line="214"/>
<source>File contains:</source>
<translation type="unfinished"></translation>
<translation>Datei enthält:</translation>
</message>
<message>
<location filename="../filesearch.ui" line="223"/>
<source>Case insensiti&amp;ve</source>
<translation type="unfinished"></translation>
<translation>Groß- und Kleinschreibung ig&amp;norieren</translation>
</message>
<message>
<location filename="../filesearch.ui" line="230"/>
<source>&amp;Use regular expression</source>
<translation type="unfinished"></translation>
<translation>Reg&amp;ulären Ausdruck verwenden</translation>
</message>
<message>
<location filename="../filesearch.ui" line="254"/>
<source>Properties</source>
<translation type="unfinished">Eigenschaften</translation>
<translation>Eigenschaften</translation>
</message>
<message>
<location filename="../filesearch.ui" line="260"/>
<source>File Size:</source>
<translation type="unfinished"></translation>
<translation>Dateigröße:</translation>
</message>
<message>
<location filename="../filesearch.ui" line="266"/>
<source>Larger than:</source>
<translation type="unfinished"></translation>
<translation>Größer als:</translation>
</message>
<message>
<location filename="../filesearch.ui" line="282"/>
<location filename="../filesearch.ui" line="323"/>
<source>Bytes</source>
<translation type="unfinished"></translation>
<translation>Bytes</translation>
</message>
<message>
<location filename="../filesearch.ui" line="287"/>
<location filename="../filesearch.ui" line="328"/>
<source>KiB</source>
<translation type="unfinished"></translation>
<translation>KiB</translation>
</message>
<message>
<location filename="../filesearch.ui" line="292"/>
<location filename="../filesearch.ui" line="333"/>
<source>MiB</source>
<translation type="unfinished"></translation>
<translation>MiB</translation>
</message>
<message>
<location filename="../filesearch.ui" line="297"/>
<location filename="../filesearch.ui" line="338"/>
<source>GiB</source>
<translation type="unfinished"></translation>
<translation>GiB</translation>
</message>
<message>
<location filename="../filesearch.ui" line="307"/>
<source>Smaller than:</source>
<translation type="unfinished"></translation>
<translation>Kleiner als:</translation>
</message>
<message>
<location filename="../filesearch.ui" line="351"/>
<source>Last Modified Time:</source>
<translation type="unfinished"></translation>
<translation>Letztes Änderungsdatum:</translation>
</message>
<message>
<location filename="../filesearch.ui" line="357"/>
<source>Earlier than:</source>
<translation type="unfinished"></translation>
<translation>Früher als:</translation>
</message>
<message>
<location filename="../filesearch.ui" line="364"/>
<source>Later than:</source>
<translation type="unfinished"></translation>
<translation>Später als:</translation>
</message>
</context>
</TS>

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="zh_TW">
<TS version="2.1" language="lt_LT">
<context>
<name>AppChooserDialog</name>
<message>

@ -0,0 +1,286 @@
/*
* Copyright (C) 2016 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 <QtGlobal>
#include "xdndworkaround.h"
#include <QApplication>
#include <QDebug>
#include <QX11Info>
#include <QMimeData>
#include <QCursor>
#include <QWidget>
// This part is for Qt >= 5.4 only
#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
#include <QDrag>
#include <QUrl>
#include <string.h>
// these are private Qt headers which are not part of Qt APIs
#include <private/qdnd_p.h> // 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<xcb_generic_event_t *>(message);
uint8_t response_type = event->response_type & uint8_t(~0x80);
switch(event->response_type & ~0x80) {
case XCB_CLIENT_MESSAGE:
return clientMessage(reinterpret_cast<xcb_client_message_event_t*>(event));
case XCB_SELECTION_NOTIFY:
return selectionNotify(reinterpret_cast<xcb_selection_notify_event_t*>(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<xcb_selection_request_event_t*>(event));
case XCB_GE_GENERIC:
// newer versions of Qt5 supports xinput2, which sends its mouse events via XGE.
return genericEvent(reinterpret_cast<xcb_ge_generic_event_t*>(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<QUrl> 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 *)&notify);
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)

@ -0,0 +1,90 @@
/*
* Copyright (C) 2016 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
*
*/
/*
* 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 <QtGlobal>
#include <QObject>
#include <QAbstractNativeEventFilter>
#include <xcb/xcb.h>
#include <QByteArray>
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
Loading…
Cancel
Save