Manual merge the new release 0.11.0 from debian/experimental
This commit is contained in:
commit
108dbfc235
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@ -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
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
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
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}>"
|
||||
)
|
||||
|
||||
|
49
src/customaction_p.h
Normal file
49
src/customaction_p.h
Normal file
@ -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 "%1" 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&ab</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Otevřít v novém &panelu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../dirtreeview.cpp" line="212"/>
|
||||
<source>Open in New Win&dow</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Otevřít v novém &okně</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../dirtreeview.cpp" line="217"/>
|
||||
<source>Open in Termina&l</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Otevřít v &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>&Connect</source>
|
||||
<translation type="unfinished">&Připojit</translation>
|
||||
<translation>&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>&Overwrite</source>
|
||||
<translation type="unfinished">&Přepsat</translation>
|
||||
<translation>&Přepsat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../renamedialog.cpp" line="91"/>
|
||||
<source>&Rename</source>
|
||||
<translation type="unfinished">Pře&jmenovat</translation>
|
||||
<translation>Pře&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&ser:</source>
|
||||
<translation type="unfinished">Připojit jako &uživatel:</translation>
|
||||
<translation>Připojit jako &uživatel:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mount-operation-password.ui" line="79"/>
|
||||
<source>&Username:</source>
|
||||
<translation type="unfinished">Uživatelské &jméno:</translation>
|
||||
<translation>&Jméno:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mount-operation-password.ui" line="102"/>
|
||||
<source>&Password:</source>
|
||||
<translation type="unfinished">&Heslo:</translation>
|
||||
<translation>&Heslo:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mount-operation-password.ui" line="112"/>
|
||||
<source>&Domain:</source>
|
||||
<translation type="unfinished">&Doména:</translation>
|
||||
<translation>&Doména:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mount-operation-password.ui" line="127"/>
|
||||
<source>Forget password &immediately</source>
|
||||
<translation type="unfinished">&Zapomenout heslo</translation>
|
||||
<translation>&Zapomenout heslo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mount-operation-password.ui" line="137"/>
|
||||
<source>Remember password until you &logout</source>
|
||||
<translation type="unfinished">Pamatovat si heslo do &odhlášení</translation>
|
||||
<translation>Pamatovat si heslo do &odhlášení</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../mount-operation-password.ui" line="147"/>
|
||||
<source>Remember &forever</source>
|
||||
<translation type="unfinished">Pamatovat si heslo &trvale</translation>
|
||||
<translation>Pamatovat si heslo &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>&Add</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>&Přidat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../filesearch.ui" line="90"/>
|
||||
<source>&Remove</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>&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&ve</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Nezohledňovat &velikost písmen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../filesearch.ui" line="230"/>
|
||||
<source>&Use regular expression</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Po&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>&Add</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>&Hinzufügen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../filesearch.ui" line="90"/>
|
||||
<source>&Remove</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>&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&ve</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Groß- und Kleinschreibung ig&norieren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../filesearch.ui" line="230"/>
|
||||
<source>&Use regular expression</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Reg&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>
|
||||
|
286
src/xdndworkaround.cpp
Normal file
286
src/xdndworkaround.cpp
Normal file
@ -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 *)¬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)
|
90
src/xdndworkaround.h
Normal file
90
src/xdndworkaround.h
Normal file
@ -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…
x
Reference in New Issue
Block a user