parent
356dcd1014
commit
faaa439846
@ -1,150 +0,0 @@
|
|||||||
project(fm-qt)
|
|
||||||
|
|
||||||
set(LIBRARY_NAME "fm-qt5")
|
|
||||||
set(QTX_INCLUDE_DIRS "")
|
|
||||||
set(QTX_LIBRARIES Qt5::Widgets Qt5::X11Extras)
|
|
||||||
set(libfm_TRANSLATION_TEMPLATE "lib${PROJECT_NAME}")
|
|
||||||
|
|
||||||
include_directories(
|
|
||||||
${QTX_INCLUDE_DIRS}
|
|
||||||
${LIBFM_INCLUDE_DIRS}
|
|
||||||
"${LIBFM_INCLUDEDIR}/libfm" # to workaround incorrect #include in fm-actions.
|
|
||||||
${LIBMENUCACHE_INCLUDE_DIRS}
|
|
||||||
${SYSTEM_LIBS_INCLUDE_DIRS}
|
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
|
||||||
)
|
|
||||||
|
|
||||||
link_directories(
|
|
||||||
${LIBFM_LIBRARY_DIRS}
|
|
||||||
${LIBMENUCACHE_LIBRARY_DIRS}
|
|
||||||
${SYSTEM_LIBS_LIBRARY_DIRS}
|
|
||||||
)
|
|
||||||
|
|
||||||
set(libfm_SRCS
|
|
||||||
libfmqt.cpp
|
|
||||||
bookmarkaction.cpp
|
|
||||||
sidepane.cpp
|
|
||||||
icontheme.cpp
|
|
||||||
filelauncher.cpp
|
|
||||||
foldermodel.cpp
|
|
||||||
foldermodelitem.cpp
|
|
||||||
cachedfoldermodel.cpp
|
|
||||||
proxyfoldermodel.cpp
|
|
||||||
folderview.cpp
|
|
||||||
folderitemdelegate.cpp
|
|
||||||
createnewmenu.cpp
|
|
||||||
filemenu.cpp
|
|
||||||
foldermenu.cpp
|
|
||||||
filepropsdialog.cpp
|
|
||||||
applaunchcontext.cpp
|
|
||||||
placesview.cpp
|
|
||||||
placesmodel.cpp
|
|
||||||
placesmodelitem.cpp
|
|
||||||
dirtreeview.cpp
|
|
||||||
dirtreemodel.cpp
|
|
||||||
dirtreemodelitem.cpp
|
|
||||||
dnddest.cpp
|
|
||||||
mountoperation.cpp
|
|
||||||
mountoperationpassworddialog.cpp
|
|
||||||
mountoperationquestiondialog.cpp
|
|
||||||
fileoperation.cpp
|
|
||||||
fileoperationdialog.cpp
|
|
||||||
renamedialog.cpp
|
|
||||||
pathedit.cpp
|
|
||||||
colorbutton.cpp
|
|
||||||
fontbutton.cpp
|
|
||||||
browsehistory.cpp
|
|
||||||
utilities.cpp
|
|
||||||
dndactionmenu.cpp
|
|
||||||
editbookmarksdialog.cpp
|
|
||||||
thumbnailloader.cpp
|
|
||||||
path.cpp
|
|
||||||
execfiledialog.cpp
|
|
||||||
appchoosercombobox.cpp
|
|
||||||
appmenuview.cpp
|
|
||||||
appchooserdialog.cpp
|
|
||||||
filesearchdialog.cpp
|
|
||||||
fm-search.c # might be moved to libfm later
|
|
||||||
)
|
|
||||||
|
|
||||||
set(libfm_UIS
|
|
||||||
file-props.ui
|
|
||||||
file-operation-dialog.ui
|
|
||||||
rename-dialog.ui
|
|
||||||
mount-operation-password.ui
|
|
||||||
edit-bookmarks.ui
|
|
||||||
exec-file.ui
|
|
||||||
app-chooser-dialog.ui
|
|
||||||
filesearch.ui
|
|
||||||
)
|
|
||||||
|
|
||||||
qt5_wrap_ui(libfm_UIS_H ${libfm_UIS})
|
|
||||||
|
|
||||||
# add translation for libfm-qt
|
|
||||||
lxqt_translate_ts(QM_FILES
|
|
||||||
UPDATE_TRANSLATIONS ${UPDATE_TRANSLATIONS}
|
|
||||||
SOURCES ${libfm_SRCS} ${libfm_UIS}
|
|
||||||
TEMPLATE ${libfm_TRANSLATION_TEMPLATE}
|
|
||||||
)
|
|
||||||
|
|
||||||
add_library(${LIBRARY_NAME} SHARED
|
|
||||||
${libfm_SRCS}
|
|
||||||
${libfm_UIS_H}
|
|
||||||
${QM_FILES}
|
|
||||||
)
|
|
||||||
|
|
||||||
set_property(
|
|
||||||
TARGET ${LIBRARY_NAME} APPEND
|
|
||||||
PROPERTY COMPILE_DEFINITIONS
|
|
||||||
LIBFM_QT_COMPILATION=1
|
|
||||||
LIBFM_DATA_DIR="${CMAKE_INSTALL_FULL_DATADIR}/libfm-qt"
|
|
||||||
)
|
|
||||||
|
|
||||||
# only turn on custom actions support if it is enabled in libfm.
|
|
||||||
if(EXISTS ${LIBFM_INCLUDEDIR}/libfm/fm-actions.h)
|
|
||||||
set_property(TARGET ${LIBRARY_NAME} APPEND PROPERTY COMPILE_DEFINITIONS CUSTOM_ACTIONS)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_link_libraries(${LIBRARY_NAME}
|
|
||||||
${QTX_LIBRARIES}
|
|
||||||
${LIBFM_LIBRARIES}
|
|
||||||
${LIBMENUCACHE_LIBRARIES}
|
|
||||||
${SYSTEM_LIBS_LIBRARIES}
|
|
||||||
)
|
|
||||||
|
|
||||||
# set libtool soname
|
|
||||||
set_target_properties(${LIBRARY_NAME} PROPERTIES
|
|
||||||
VERSION ${LIBFM_QT_LIB_VERSION}
|
|
||||||
SOVERSION ${LIBFM_QT_LIB_SOVERSION}
|
|
||||||
)
|
|
||||||
|
|
||||||
# install include header files (FIXME: can we make this cleaner? should dir name be versioned?)
|
|
||||||
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
|
||||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
|
||||||
FILES_MATCHING PATTERN "*.h"
|
|
||||||
PATTERN "*_p.h" EXCLUDE # exclude private headers
|
|
||||||
)
|
|
||||||
|
|
||||||
# FIXME: add libtool version to the lib (soname) later.
|
|
||||||
# FIXME: only export public symbols
|
|
||||||
|
|
||||||
install(TARGETS ${LIBRARY_NAME}
|
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
PUBLIC_HEADER
|
|
||||||
)
|
|
||||||
|
|
||||||
# install a pkgconfig file for libfm-qt
|
|
||||||
set(REQUIRED_QT "Qt5Core >= 5.1 Qt5DBus >= 5.1")
|
|
||||||
configure_file(libfm-qt.pc.in lib${LIBRARY_NAME}.pc @ONLY)
|
|
||||||
# FreeBSD loves to install files to different locations
|
|
||||||
# http://www.freebsd.org/doc/handbook/dirstructure.html
|
|
||||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
|
|
||||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/lib${LIBRARY_NAME}.pc" DESTINATION libdata/pkgconfig)
|
|
||||||
else()
|
|
||||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/lib${LIBRARY_NAME}.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
install(FILES ${QM_FILES} DESTINATION "${CMAKE_INSTALL_DATADIR}/libfm-qt/translations")
|
|
||||||
|
|
||||||
# prevent the generated files from being deleted during make cleaner
|
|
||||||
set_directory_properties(PROPERTIES CLEAN_NO_CUSTOM true)
|
|
@ -1,183 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>AppChooserDialog</class>
|
|
||||||
<widget class="QDialog" name="AppChooserDialog">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>432</width>
|
|
||||||
<height>387</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Choose an Application</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QFormLayout" name="formLayout">
|
|
||||||
<property name="fieldGrowthPolicy">
|
|
||||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
|
||||||
</property>
|
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QLabel" name="fileTypeHeader"/>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0" colspan="2">
|
|
||||||
<widget class="QTabWidget" name="tabWidget">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>1</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="currentIndex">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="tab">
|
|
||||||
<attribute name="title">
|
|
||||||
<string>Installed Applications</string>
|
|
||||||
</attribute>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="Fm::AppMenuView" name="appMenuView"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="tab_2">
|
|
||||||
<attribute name="title">
|
|
||||||
<string>Custom Command</string>
|
|
||||||
</attribute>
|
|
||||||
<layout class="QFormLayout" name="formLayout_2">
|
|
||||||
<item row="0" column="0" colspan="2">
|
|
||||||
<widget class="QLabel" name="label_3">
|
|
||||||
<property name="text">
|
|
||||||
<string>Command line to execute:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0" colspan="2">
|
|
||||||
<widget class="QLineEdit" name="cmdLine"/>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QLabel" name="label_4">
|
|
||||||
<property name="text">
|
|
||||||
<string>Application name:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1">
|
|
||||||
<widget class="QLineEdit" name="appName"/>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0" colspan="2">
|
|
||||||
<widget class="QLabel" name="label_5">
|
|
||||||
<property name="text">
|
|
||||||
<string><b>These special codes can be used in the command line:</b>
|
|
||||||
<ul>
|
|
||||||
<li><b>%f</b>: Represents a single file name</li>
|
|
||||||
<li><b>%F</b>: Represents multiple file names</li>
|
|
||||||
<li><b>%u</b>: Represents a single URI of the file</li>
|
|
||||||
<li><b>%U</b>: Represents multiple URIs</li>
|
|
||||||
</ul></string>
|
|
||||||
</property>
|
|
||||||
<property name="textFormat">
|
|
||||||
<enum>Qt::RichText</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="0" colspan="2">
|
|
||||||
<widget class="QCheckBox" name="keepTermOpen">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Keep terminal window open after command execution</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="0" colspan="2">
|
|
||||||
<widget class="QCheckBox" name="useTerminal">
|
|
||||||
<property name="text">
|
|
||||||
<string>Execute in terminal emulator</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0" colspan="2">
|
|
||||||
<widget class="QCheckBox" name="setDefault">
|
|
||||||
<property name="text">
|
|
||||||
<string>Set selected application as default action of this file type</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0" colspan="2">
|
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="standardButtons">
|
|
||||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<customwidgets>
|
|
||||||
<customwidget>
|
|
||||||
<class>Fm::AppMenuView</class>
|
|
||||||
<extends>QTreeView</extends>
|
|
||||||
<header>appmenuview.h</header>
|
|
||||||
</customwidget>
|
|
||||||
</customwidgets>
|
|
||||||
<resources/>
|
|
||||||
<connections>
|
|
||||||
<connection>
|
|
||||||
<sender>buttonBox</sender>
|
|
||||||
<signal>accepted()</signal>
|
|
||||||
<receiver>AppChooserDialog</receiver>
|
|
||||||
<slot>accept()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>227</x>
|
|
||||||
<y>359</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>157</x>
|
|
||||||
<y>274</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
<connection>
|
|
||||||
<sender>buttonBox</sender>
|
|
||||||
<signal>rejected()</signal>
|
|
||||||
<receiver>AppChooserDialog</receiver>
|
|
||||||
<slot>reject()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>295</x>
|
|
||||||
<y>365</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>286</x>
|
|
||||||
<y>274</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
<connection>
|
|
||||||
<sender>useTerminal</sender>
|
|
||||||
<signal>toggled(bool)</signal>
|
|
||||||
<receiver>keepTermOpen</receiver>
|
|
||||||
<slot>setEnabled(bool)</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>72</x>
|
|
||||||
<y>260</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>79</x>
|
|
||||||
<y>282</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
</connections>
|
|
||||||
</ui>
|
|
@ -1,154 +0,0 @@
|
|||||||
/*
|
|
||||||
* <one line to give the library's name and an idea of what it does.>
|
|
||||||
* Copyright (C) 2014 <copyright holder> <email>
|
|
||||||
*
|
|
||||||
* 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 "appchoosercombobox.h"
|
|
||||||
#include "icontheme.h"
|
|
||||||
#include "appchooserdialog.h"
|
|
||||||
#include "utilities.h"
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
AppChooserComboBox::AppChooserComboBox(QWidget* parent):
|
|
||||||
QComboBox(parent),
|
|
||||||
defaultApp_(NULL),
|
|
||||||
appInfos_(NULL),
|
|
||||||
defaultAppIndex_(-1),
|
|
||||||
prevIndex_(0),
|
|
||||||
mimeType_(NULL),
|
|
||||||
blockOnCurrentIndexChanged_(false) {
|
|
||||||
|
|
||||||
// the new Qt5 signal/slot syntax cannot handle overloaded methods by default
|
|
||||||
// hence a type-casting is needed here. really ugly!
|
|
||||||
// reference: http://qt-project.org/forums/viewthread/21513
|
|
||||||
connect((QComboBox*)this, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &AppChooserComboBox::onCurrentIndexChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
AppChooserComboBox::~AppChooserComboBox() {
|
|
||||||
if(mimeType_)
|
|
||||||
fm_mime_type_unref(mimeType_);
|
|
||||||
if(defaultApp_)
|
|
||||||
g_object_unref(defaultApp_);
|
|
||||||
// delete GAppInfo objects stored for Combobox
|
|
||||||
if(appInfos_) {
|
|
||||||
g_list_foreach(appInfos_, (GFunc)g_object_unref, NULL);
|
|
||||||
g_list_free(appInfos_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AppChooserComboBox::setMimeType(FmMimeType* mimeType) {
|
|
||||||
clear();
|
|
||||||
if(mimeType_)
|
|
||||||
fm_mime_type_unref(mimeType_);
|
|
||||||
|
|
||||||
mimeType_ = fm_mime_type_ref(mimeType);
|
|
||||||
if(mimeType_) {
|
|
||||||
const char* typeName = fm_mime_type_get_type(mimeType_);
|
|
||||||
defaultApp_ = g_app_info_get_default_for_type(typeName, FALSE);
|
|
||||||
appInfos_ = g_app_info_get_all_for_type(typeName);
|
|
||||||
int i = 0;
|
|
||||||
for(GList* l = appInfos_; l; l = l->next, ++i) {
|
|
||||||
GAppInfo* app = G_APP_INFO(l->data);
|
|
||||||
GIcon* gicon = g_app_info_get_icon(app);
|
|
||||||
QString name = QString::fromUtf8(g_app_info_get_name(app));
|
|
||||||
// QVariant data = qVariantFromValue<void*>(app);
|
|
||||||
// addItem(IconTheme::icon(gicon), name, data);
|
|
||||||
addItem(IconTheme::icon(gicon), name);
|
|
||||||
if(g_app_info_equal(app, defaultApp_))
|
|
||||||
defaultAppIndex_ = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// add "Other applications" item
|
|
||||||
insertSeparator(count());
|
|
||||||
addItem(tr("Customize"));
|
|
||||||
if(defaultAppIndex_ != -1)
|
|
||||||
setCurrentIndex(defaultAppIndex_);
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns the currently selected app.
|
|
||||||
GAppInfo* AppChooserComboBox::selectedApp() {
|
|
||||||
return G_APP_INFO(g_list_nth_data(appInfos_, currentIndex()));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AppChooserComboBox::isChanged() {
|
|
||||||
return (defaultAppIndex_ != currentIndex());
|
|
||||||
}
|
|
||||||
|
|
||||||
void AppChooserComboBox::onCurrentIndexChanged(int index) {
|
|
||||||
if(index == -1 || index == prevIndex_ || blockOnCurrentIndexChanged_)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// the last item is "Customize"
|
|
||||||
if(index == (count() - 1)) {
|
|
||||||
/* TODO: let the user choose an app or add custom actions here. */
|
|
||||||
QWidget* toplevel = topLevelWidget();
|
|
||||||
AppChooserDialog dlg(mimeType_, toplevel);
|
|
||||||
dlg.setWindowModality(Qt::WindowModal);
|
|
||||||
dlg.setCanSetDefault(false);
|
|
||||||
if(dlg.exec() == QDialog::Accepted) {
|
|
||||||
GAppInfo* app = dlg.selectedApp();
|
|
||||||
if(app) {
|
|
||||||
/* see if it's already in the list to prevent duplication */
|
|
||||||
GList* found = NULL;
|
|
||||||
for(found = appInfos_; found; found = found->next) {
|
|
||||||
if(g_app_info_equal(app, G_APP_INFO(found->data)))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// inserting new items or change current index will recursively trigger onCurrentIndexChanged.
|
|
||||||
// we need to block our handler to prevent recursive calls.
|
|
||||||
blockOnCurrentIndexChanged_ = true;
|
|
||||||
/* if it's already in the list, select it */
|
|
||||||
if(found) {
|
|
||||||
setCurrentIndex(g_list_position(appInfos_, found));
|
|
||||||
g_object_unref(app);
|
|
||||||
}
|
|
||||||
else { /* if it's not found, add it to the list */
|
|
||||||
appInfos_ = g_list_prepend(appInfos_, app);
|
|
||||||
GIcon* gicon = g_app_info_get_icon(app);
|
|
||||||
QString name = QString::fromUtf8(g_app_info_get_name(app));
|
|
||||||
insertItem(0, IconTheme::icon(gicon), name);
|
|
||||||
setCurrentIndex(0);
|
|
||||||
}
|
|
||||||
blockOnCurrentIndexChanged_ = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// block our handler to prevent recursive calls.
|
|
||||||
blockOnCurrentIndexChanged_ = true;
|
|
||||||
// restore to previously selected item
|
|
||||||
setCurrentIndex(prevIndex_);
|
|
||||||
blockOnCurrentIndexChanged_ = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
prevIndex_ = index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
/* get a list of custom apps added with app-chooser.
|
|
||||||
* the returned GList is owned by the combo box and shouldn't be freed. */
|
|
||||||
const GList* AppChooserComboBox::customApps() {
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
} // namespace Fm
|
|
@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
* <one line to give the library's name and an idea of what it does.>
|
|
||||||
* Copyright (C) 2014 <copyright holder> <email>
|
|
||||||
*
|
|
||||||
* 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_APPCHOOSERCOMBOBOX_H
|
|
||||||
#define FM_APPCHOOSERCOMBOBOX_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QComboBox>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class LIBFM_QT_API AppChooserComboBox : public QComboBox {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
~AppChooserComboBox();
|
|
||||||
AppChooserComboBox(QWidget* parent);
|
|
||||||
|
|
||||||
void setMimeType(FmMimeType* mimeType);
|
|
||||||
|
|
||||||
FmMimeType* mimeType() {
|
|
||||||
return mimeType_;
|
|
||||||
}
|
|
||||||
|
|
||||||
GAppInfo* selectedApp();
|
|
||||||
// const GList* customApps();
|
|
||||||
|
|
||||||
bool isChanged();
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void onCurrentIndexChanged(int index);
|
|
||||||
|
|
||||||
private:
|
|
||||||
FmMimeType* mimeType_;
|
|
||||||
GList* appInfos_; // applications used to open the file type
|
|
||||||
GAppInfo* defaultApp_; // default application used to open the file type
|
|
||||||
int defaultAppIndex_;
|
|
||||||
int prevIndex_;
|
|
||||||
bool blockOnCurrentIndexChanged_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_APPCHOOSERCOMBOBOX_H
|
|
@ -1,286 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2014 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
* Copyright 2012-2013 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
|
|
||||||
*
|
|
||||||
* 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 "appchooserdialog.h"
|
|
||||||
#include "ui_app-chooser-dialog.h"
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <gio/gdesktopappinfo.h>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
AppChooserDialog::AppChooserDialog(FmMimeType* mimeType, QWidget* parent, Qt::WindowFlags f):
|
|
||||||
QDialog(parent, f),
|
|
||||||
mimeType_(NULL),
|
|
||||||
selectedApp_(NULL),
|
|
||||||
canSetDefault_(true),
|
|
||||||
ui(new Ui::AppChooserDialog()) {
|
|
||||||
ui->setupUi(this);
|
|
||||||
|
|
||||||
connect(ui->appMenuView, &AppMenuView::selectionChanged, this, &AppChooserDialog::onSelectionChanged);
|
|
||||||
connect(ui->tabWidget, &QTabWidget::currentChanged, this, &AppChooserDialog::onTabChanged);
|
|
||||||
|
|
||||||
if(!ui->appMenuView->isAppSelected())
|
|
||||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); // disable OK button
|
|
||||||
|
|
||||||
if(mimeType)
|
|
||||||
setMimeType(mimeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
AppChooserDialog::~AppChooserDialog() {
|
|
||||||
delete ui;
|
|
||||||
if(mimeType_)
|
|
||||||
fm_mime_type_unref(mimeType_);
|
|
||||||
if(selectedApp_)
|
|
||||||
g_object_unref(selectedApp_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AppChooserDialog::isSetDefault() {
|
|
||||||
return ui->setDefault->isChecked();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void on_temp_appinfo_destroy(gpointer data, GObject* objptr) {
|
|
||||||
char* filename = (char*)data;
|
|
||||||
if(g_unlink(filename) < 0)
|
|
||||||
g_critical("failed to remove %s", filename);
|
|
||||||
/* else
|
|
||||||
qDebug("temp file %s removed", filename); */
|
|
||||||
g_free(filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
static GAppInfo* app_info_create_from_commandline(const char* commandline,
|
|
||||||
const char* application_name,
|
|
||||||
const char* bin_name,
|
|
||||||
const char* mime_type,
|
|
||||||
gboolean terminal, gboolean keep) {
|
|
||||||
GAppInfo* app = NULL;
|
|
||||||
char* dirname = g_build_filename(g_get_user_data_dir(), "applications", NULL);
|
|
||||||
const char* app_basename = strrchr(bin_name, '/');
|
|
||||||
|
|
||||||
if(app_basename)
|
|
||||||
app_basename++;
|
|
||||||
else
|
|
||||||
app_basename = bin_name;
|
|
||||||
if(g_mkdir_with_parents(dirname, 0700) == 0) {
|
|
||||||
char* filename = g_strdup_printf("%s/userapp-%s-XXXXXX.desktop", dirname, app_basename);
|
|
||||||
int fd = g_mkstemp(filename);
|
|
||||||
if(fd != -1) {
|
|
||||||
GString* content = g_string_sized_new(256);
|
|
||||||
g_string_printf(content,
|
|
||||||
"[" G_KEY_FILE_DESKTOP_GROUP "]\n"
|
|
||||||
G_KEY_FILE_DESKTOP_KEY_TYPE "=" G_KEY_FILE_DESKTOP_TYPE_APPLICATION "\n"
|
|
||||||
G_KEY_FILE_DESKTOP_KEY_NAME "=%s\n"
|
|
||||||
G_KEY_FILE_DESKTOP_KEY_EXEC "=%s\n"
|
|
||||||
G_KEY_FILE_DESKTOP_KEY_CATEGORIES "=Other;\n"
|
|
||||||
G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY "=true\n",
|
|
||||||
application_name,
|
|
||||||
commandline
|
|
||||||
);
|
|
||||||
if(mime_type)
|
|
||||||
g_string_append_printf(content,
|
|
||||||
G_KEY_FILE_DESKTOP_KEY_MIME_TYPE "=%s\n",
|
|
||||||
mime_type);
|
|
||||||
g_string_append_printf(content,
|
|
||||||
G_KEY_FILE_DESKTOP_KEY_TERMINAL "=%s\n",
|
|
||||||
terminal ? "true" : "false");
|
|
||||||
if(terminal)
|
|
||||||
g_string_append_printf(content, "X-KeepTerminal=%s\n",
|
|
||||||
keep ? "true" : "false");
|
|
||||||
close(fd); /* g_file_set_contents() may fail creating duplicate */
|
|
||||||
if(g_file_set_contents(filename, content->str, content->len, NULL)) {
|
|
||||||
char* fbname = g_path_get_basename(filename);
|
|
||||||
app = G_APP_INFO(g_desktop_app_info_new(fbname));
|
|
||||||
g_free(fbname);
|
|
||||||
/* if there is mime_type set then created application will be
|
|
||||||
saved for the mime type (see fm_choose_app_for_mime_type()
|
|
||||||
below) but if not then we should remove this temp. file */
|
|
||||||
if(!mime_type || !application_name[0])
|
|
||||||
/* save the name so this file will be removed later */
|
|
||||||
g_object_weak_ref(G_OBJECT(app), on_temp_appinfo_destroy,
|
|
||||||
g_strdup(filename));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
g_unlink(filename);
|
|
||||||
g_string_free(content, TRUE);
|
|
||||||
}
|
|
||||||
g_free(filename);
|
|
||||||
}
|
|
||||||
g_free(dirname);
|
|
||||||
return app;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline static char* get_binary(const char* cmdline, gboolean* arg_found) {
|
|
||||||
/* see if command line contains %f, %F, %u, or %U. */
|
|
||||||
const char* p = strstr(cmdline, " %");
|
|
||||||
if(p) {
|
|
||||||
if(!strchr("fFuU", *(p + 2)))
|
|
||||||
p = NULL;
|
|
||||||
}
|
|
||||||
if(arg_found)
|
|
||||||
*arg_found = (p != NULL);
|
|
||||||
if(p)
|
|
||||||
return g_strndup(cmdline, p - cmdline);
|
|
||||||
else
|
|
||||||
return g_strdup(cmdline);
|
|
||||||
}
|
|
||||||
|
|
||||||
GAppInfo* AppChooserDialog::customCommandToApp() {
|
|
||||||
GAppInfo* app = NULL;
|
|
||||||
QByteArray cmdline = ui->cmdLine->text().toLocal8Bit();
|
|
||||||
QByteArray app_name = ui->appName->text().toUtf8();
|
|
||||||
if(!cmdline.isEmpty()) {
|
|
||||||
gboolean arg_found = FALSE;
|
|
||||||
char* bin1 = get_binary(cmdline.constData(), &arg_found);
|
|
||||||
qDebug("bin1 = %s", bin1);
|
|
||||||
/* see if command line contains %f, %F, %u, or %U. */
|
|
||||||
if(!arg_found) { /* append %f if no %f, %F, %u, or %U was found. */
|
|
||||||
cmdline += " %f";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXME: is there any better way to do this? */
|
|
||||||
/* We need to ensure that no duplicated items are added */
|
|
||||||
if(mimeType_) {
|
|
||||||
MenuCache* menu_cache;
|
|
||||||
/* see if the command is already in the list of known apps for this mime-type */
|
|
||||||
GList* apps = g_app_info_get_all_for_type(fm_mime_type_get_type(mimeType_));
|
|
||||||
GList* l;
|
|
||||||
for(l = apps; l; l = l->next) {
|
|
||||||
GAppInfo* app2 = G_APP_INFO(l->data);
|
|
||||||
const char* cmd = g_app_info_get_commandline(app2);
|
|
||||||
char* bin2 = get_binary(cmd, NULL);
|
|
||||||
if(g_strcmp0(bin1, bin2) == 0) {
|
|
||||||
app = G_APP_INFO(g_object_ref(app2));
|
|
||||||
qDebug("found in app list");
|
|
||||||
g_free(bin2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
g_free(bin2);
|
|
||||||
}
|
|
||||||
g_list_foreach(apps, (GFunc)g_object_unref, NULL);
|
|
||||||
g_list_free(apps);
|
|
||||||
if(app)
|
|
||||||
goto _out;
|
|
||||||
|
|
||||||
/* see if this command can be found in menu cache */
|
|
||||||
menu_cache = menu_cache_lookup("applications.menu");
|
|
||||||
if(menu_cache) {
|
|
||||||
MenuCacheDir* root_dir = menu_cache_dup_root_dir(menu_cache);
|
|
||||||
if(root_dir) {
|
|
||||||
GSList* all_apps = menu_cache_list_all_apps(menu_cache);
|
|
||||||
GSList* l;
|
|
||||||
for(l = all_apps; l; l = l->next) {
|
|
||||||
MenuCacheApp* ma = MENU_CACHE_APP(l->data);
|
|
||||||
const char* exec = menu_cache_app_get_exec(ma);
|
|
||||||
char* bin2;
|
|
||||||
if(exec == NULL) {
|
|
||||||
g_warning("application %s has no Exec statement", menu_cache_item_get_id(MENU_CACHE_ITEM(ma)));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
bin2 = get_binary(exec, NULL);
|
|
||||||
if(g_strcmp0(bin1, bin2) == 0) {
|
|
||||||
app = G_APP_INFO(g_desktop_app_info_new(menu_cache_item_get_id(MENU_CACHE_ITEM(ma))));
|
|
||||||
qDebug("found in menu cache");
|
|
||||||
menu_cache_item_unref(MENU_CACHE_ITEM(ma));
|
|
||||||
g_free(bin2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
menu_cache_item_unref(MENU_CACHE_ITEM(ma));
|
|
||||||
g_free(bin2);
|
|
||||||
}
|
|
||||||
g_slist_free(all_apps);
|
|
||||||
menu_cache_item_unref(MENU_CACHE_ITEM(root_dir));
|
|
||||||
}
|
|
||||||
menu_cache_unref(menu_cache);
|
|
||||||
}
|
|
||||||
if(app)
|
|
||||||
goto _out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXME: g_app_info_create_from_commandline force the use of %f or %u, so this is not we need */
|
|
||||||
app = app_info_create_from_commandline(cmdline.constData(), app_name.constData(), bin1,
|
|
||||||
mimeType_ ? fm_mime_type_get_type(mimeType_) : NULL,
|
|
||||||
ui->useTerminal->isChecked(), ui->keepTermOpen->isChecked());
|
|
||||||
_out:
|
|
||||||
g_free(bin1);
|
|
||||||
}
|
|
||||||
return app;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AppChooserDialog::accept() {
|
|
||||||
QDialog::accept();
|
|
||||||
|
|
||||||
if(ui->tabWidget->currentIndex() == 0) {
|
|
||||||
selectedApp_ = ui->appMenuView->selectedApp();
|
|
||||||
}
|
|
||||||
else { // custom command line
|
|
||||||
selectedApp_ = customCommandToApp();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(selectedApp_) {
|
|
||||||
if(mimeType_ && fm_mime_type_get_type(mimeType_) && g_app_info_get_name(selectedApp_)[0]) {
|
|
||||||
/* add this app to the mime-type */
|
|
||||||
#if GLIB_CHECK_VERSION(2, 27, 6)
|
|
||||||
g_app_info_set_as_last_used_for_type(selectedApp_, fm_mime_type_get_type(mimeType_), NULL);
|
|
||||||
#else
|
|
||||||
g_app_info_add_supports_type(selectedApp_, fm_mime_type_get_type(mimeType_), NULL);
|
|
||||||
#endif
|
|
||||||
/* if need to set default */
|
|
||||||
if(ui->setDefault->isChecked())
|
|
||||||
g_app_info_set_as_default_for_type(selectedApp_, fm_mime_type_get_type(mimeType_), NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AppChooserDialog::onSelectionChanged() {
|
|
||||||
bool isAppSelected = ui->appMenuView->isAppSelected();
|
|
||||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(isAppSelected);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AppChooserDialog::setMimeType(FmMimeType* mimeType) {
|
|
||||||
if(mimeType_)
|
|
||||||
fm_mime_type_unref(mimeType_);
|
|
||||||
|
|
||||||
mimeType_ = mimeType ? fm_mime_type_ref(mimeType) : NULL;
|
|
||||||
if(mimeType_) {
|
|
||||||
QString text = tr("Select an application to open \"%1\" files")
|
|
||||||
.arg(QString::fromUtf8(fm_mime_type_get_desc(mimeType_)));
|
|
||||||
ui->fileTypeHeader->setText(text);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ui->fileTypeHeader->hide();
|
|
||||||
ui->setDefault->hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AppChooserDialog::setCanSetDefault(bool value) {
|
|
||||||
canSetDefault_ = value;
|
|
||||||
ui->setDefault->setVisible(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AppChooserDialog::onTabChanged(int index) {
|
|
||||||
if(index == 0) { // app menu view
|
|
||||||
onSelectionChanged();
|
|
||||||
}
|
|
||||||
else if(index == 1) { // custom command
|
|
||||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Fm
|
|
@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2010-2014 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
* Copyright 2012-2013 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
|
|
||||||
*
|
|
||||||
* 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_APPCHOOSERDIALOG_H
|
|
||||||
#define FM_APPCHOOSERDIALOG_H
|
|
||||||
|
|
||||||
#include <QDialog>
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
|
|
||||||
namespace Ui {
|
|
||||||
class AppChooserDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class LIBFM_QT_API AppChooserDialog : public QDialog {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit AppChooserDialog(FmMimeType* mimeType, QWidget* parent = NULL, Qt::WindowFlags f = 0);
|
|
||||||
~AppChooserDialog();
|
|
||||||
|
|
||||||
virtual void accept();
|
|
||||||
|
|
||||||
void setMimeType(FmMimeType* mimeType);
|
|
||||||
FmMimeType* mimeType() {
|
|
||||||
return mimeType_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setCanSetDefault(bool value);
|
|
||||||
bool canSetDefault() {
|
|
||||||
return canSetDefault_;
|
|
||||||
}
|
|
||||||
|
|
||||||
GAppInfo* selectedApp() {
|
|
||||||
return G_APP_INFO(g_object_ref(selectedApp_));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isSetDefault();
|
|
||||||
|
|
||||||
private:
|
|
||||||
GAppInfo* customCommandToApp();
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void onSelectionChanged();
|
|
||||||
void onTabChanged(int index);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Ui::AppChooserDialog* ui;
|
|
||||||
FmMimeType* mimeType_;
|
|
||||||
bool canSetDefault_;
|
|
||||||
GAppInfo* selectedApp_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_APPCHOOSERDIALOG_H
|
|
@ -1,61 +0,0 @@
|
|||||||
/*
|
|
||||||
<one line to give the library's name and an idea of what it does.>
|
|
||||||
Copyright (C) 2012 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 "applaunchcontext.h"
|
|
||||||
#include <QX11Info>
|
|
||||||
#include <X11/Xlib.h>
|
|
||||||
|
|
||||||
typedef struct _FmAppLaunchContext {
|
|
||||||
GAppLaunchContext parent;
|
|
||||||
}FmAppLaunchContext;
|
|
||||||
|
|
||||||
G_DEFINE_TYPE(FmAppLaunchContext, fm_app_launch_context, G_TYPE_APP_LAUNCH_CONTEXT)
|
|
||||||
|
|
||||||
static char* fm_app_launch_context_get_display(GAppLaunchContext *context, GAppInfo *info, GList *files) {
|
|
||||||
Display* dpy = QX11Info::display();
|
|
||||||
if(dpy) {
|
|
||||||
char* xstr = DisplayString(dpy);
|
|
||||||
return g_strdup(xstr);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char* fm_app_launch_context_get_startup_notify_id(GAppLaunchContext *context, GAppInfo *info, GList *files) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void fm_app_launch_context_class_init(FmAppLaunchContextClass* klass) {
|
|
||||||
GAppLaunchContextClass* app_launch_class = G_APP_LAUNCH_CONTEXT_CLASS(klass);
|
|
||||||
app_launch_class->get_display = fm_app_launch_context_get_display;
|
|
||||||
app_launch_class->get_startup_notify_id = fm_app_launch_context_get_startup_notify_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void fm_app_launch_context_init(FmAppLaunchContext* context) {
|
|
||||||
}
|
|
||||||
|
|
||||||
FmAppLaunchContext* fm_app_launch_context_new_for_widget(QWidget* widget) {
|
|
||||||
FmAppLaunchContext* context = (FmAppLaunchContext*)g_object_new(FM_TYPE_APP_LAUNCH_CONTEXT, NULL);
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
FmAppLaunchContext* fm_app_launch_context_new() {
|
|
||||||
FmAppLaunchContext* context = (FmAppLaunchContext*)g_object_new(FM_TYPE_APP_LAUNCH_CONTEXT, NULL);
|
|
||||||
return context;
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
/*
|
|
||||||
<one line to give the library's name and an idea of what it does.>
|
|
||||||
Copyright (C) 2012 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_APP_LAUNCHCONTEXT_H
|
|
||||||
#define FM_APP_LAUNCHCONTEXT_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <gio/gio.h>
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
#define FM_TYPE_APP_LAUNCH_CONTEXT (fm_app_launch_context_get_type())
|
|
||||||
#define FM_APP_LAUNCH_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\
|
|
||||||
FM_TYPE_APP_LAUNCH_CONTEXT, FmAppLaunchContext))
|
|
||||||
#define FM_APP_LAUNCH_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),\
|
|
||||||
FM_TYPE_APP_LAUNCH_CONTEXT, FmAppLaunchContextClass))
|
|
||||||
#define FM_IS_APP_LAUNCH_CONTEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),\
|
|
||||||
FM_TYPE_APP_LAUNCH_CONTEXT))
|
|
||||||
#define FM_IS_APP_LAUNCH_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),\
|
|
||||||
FM_TYPE_APP_LAUNCH_CONTEXT))
|
|
||||||
#define FM_APP_LAUNCH_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),\
|
|
||||||
FM_TYPE_APP_LAUNCH_CONTEXT, FmAppLaunchContextClass))
|
|
||||||
|
|
||||||
typedef struct _FmAppLaunchContext FmAppLaunchContext;
|
|
||||||
|
|
||||||
typedef struct _FmAppLaunchContextClass {
|
|
||||||
GAppLaunchContextClass parent;
|
|
||||||
}FmAppLaunchContextClass;
|
|
||||||
|
|
||||||
FmAppLaunchContext* fm_app_launch_context_new();
|
|
||||||
FmAppLaunchContext* fm_app_launch_context_new_for_widget(QWidget* widget);
|
|
||||||
GType fm_app_launch_context_get_type();
|
|
||||||
|
|
||||||
#endif // FM_APPLAUNCHCONTEXT_H
|
|
@ -1,159 +0,0 @@
|
|||||||
/*
|
|
||||||
* <one line to give the library's name and an idea of what it does.>
|
|
||||||
* Copyright (C) 2014 <copyright holder> <email>
|
|
||||||
*
|
|
||||||
* 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 "appmenuview.h"
|
|
||||||
#include <QStandardItemModel>
|
|
||||||
#include "icontheme.h"
|
|
||||||
#include "appmenuview_p.h"
|
|
||||||
#include <gio/gdesktopappinfo.h>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
AppMenuView::AppMenuView(QWidget* parent):
|
|
||||||
model_(new QStandardItemModel()),
|
|
||||||
menu_cache(NULL),
|
|
||||||
menu_cache_reload_notify(NULL),
|
|
||||||
QTreeView(parent) {
|
|
||||||
|
|
||||||
setHeaderHidden(true);
|
|
||||||
setSelectionMode(SingleSelection);
|
|
||||||
|
|
||||||
// initialize model
|
|
||||||
// TODO: share one model among all app menu view widgets
|
|
||||||
// ensure that we're using lxmenu-data (FIXME: should we do this?)
|
|
||||||
QByteArray oldenv = qgetenv("XDG_MENU_PREFIX");
|
|
||||||
qputenv("XDG_MENU_PREFIX", "lxde-");
|
|
||||||
menu_cache = menu_cache_lookup("applications.menu");
|
|
||||||
// if(!oldenv.isEmpty())
|
|
||||||
qputenv("XDG_MENU_PREFIX", oldenv); // restore the original value if needed
|
|
||||||
|
|
||||||
if(menu_cache) {
|
|
||||||
MenuCacheDir* dir = menu_cache_dup_root_dir(menu_cache);
|
|
||||||
menu_cache_reload_notify = menu_cache_add_reload_notify(menu_cache, _onMenuCacheReload, this);
|
|
||||||
if(dir) { /* content of menu is already loaded */
|
|
||||||
addMenuItems(NULL, dir);
|
|
||||||
menu_cache_item_unref(MENU_CACHE_ITEM(dir));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setModel(model_);
|
|
||||||
connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, &AppMenuView::selectionChanged);
|
|
||||||
selectionModel()->select(model_->index(0, 0), QItemSelectionModel::SelectCurrent);
|
|
||||||
}
|
|
||||||
|
|
||||||
AppMenuView::~AppMenuView() {
|
|
||||||
delete model_;
|
|
||||||
if(menu_cache) {
|
|
||||||
if(menu_cache_reload_notify)
|
|
||||||
menu_cache_remove_reload_notify(menu_cache, menu_cache_reload_notify);
|
|
||||||
menu_cache_unref(menu_cache);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AppMenuView::addMenuItems(QStandardItem* parentItem, MenuCacheDir* dir) {
|
|
||||||
GSList* l;
|
|
||||||
GSList* list;
|
|
||||||
/* Iterate over all menu items in this directory. */
|
|
||||||
for(l = list = menu_cache_dir_list_children(dir); l != NULL; l = l->next) {
|
|
||||||
/* Get the menu item. */
|
|
||||||
MenuCacheItem* menuItem = MENU_CACHE_ITEM(l->data);
|
|
||||||
switch(menu_cache_item_get_type(menuItem)) {
|
|
||||||
case MENU_CACHE_TYPE_NONE:
|
|
||||||
case MENU_CACHE_TYPE_SEP:
|
|
||||||
break;
|
|
||||||
case MENU_CACHE_TYPE_APP:
|
|
||||||
case MENU_CACHE_TYPE_DIR: {
|
|
||||||
AppMenuViewItem* newItem = new AppMenuViewItem(menuItem);
|
|
||||||
if(parentItem)
|
|
||||||
parentItem->insertRow(parentItem->rowCount(), newItem);
|
|
||||||
else
|
|
||||||
model_->insertRow(model_->rowCount(), newItem);
|
|
||||||
|
|
||||||
if(menu_cache_item_get_type(menuItem) == MENU_CACHE_TYPE_DIR)
|
|
||||||
addMenuItems(newItem, MENU_CACHE_DIR(menuItem));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g_slist_free_full(list, (GDestroyNotify)menu_cache_item_unref);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AppMenuView::onMenuCacheReload(MenuCache* mc) {
|
|
||||||
MenuCacheDir* dir = menu_cache_dup_root_dir(mc);
|
|
||||||
model_->clear();
|
|
||||||
/* FIXME: preserve original selection */
|
|
||||||
if(dir) {
|
|
||||||
addMenuItems(NULL, dir);
|
|
||||||
menu_cache_item_unref(MENU_CACHE_ITEM(dir));
|
|
||||||
selectionModel()->select(model_->index(0, 0), QItemSelectionModel::SelectCurrent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AppMenuView::isAppSelected() {
|
|
||||||
AppMenuViewItem* item = selectedItem();
|
|
||||||
return (item && item->isApp());
|
|
||||||
}
|
|
||||||
|
|
||||||
AppMenuViewItem* AppMenuView::selectedItem() {
|
|
||||||
QModelIndexList selected = selectedIndexes();
|
|
||||||
if(!selected.isEmpty()) {
|
|
||||||
AppMenuViewItem* item = static_cast<AppMenuViewItem*>(model_->itemFromIndex(selected.first()
|
|
||||||
));
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
GAppInfo* AppMenuView::selectedApp() {
|
|
||||||
const char* id = selectedAppDesktopId();
|
|
||||||
return id ? G_APP_INFO(g_desktop_app_info_new(id)) : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray AppMenuView::selectedAppDesktopFilePath() {
|
|
||||||
AppMenuViewItem* item = selectedItem();
|
|
||||||
if(item && item->isApp()) {
|
|
||||||
char* path = menu_cache_item_get_file_path(item->item());
|
|
||||||
QByteArray ret(path);
|
|
||||||
g_free(path);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
return QByteArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* AppMenuView::selectedAppDesktopId() {
|
|
||||||
AppMenuViewItem* item = selectedItem();
|
|
||||||
if(item && item->isApp()) {
|
|
||||||
return menu_cache_item_get_id(item->item());
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
FmPath* AppMenuView::selectedAppDesktopPath() {
|
|
||||||
AppMenuViewItem* item = selectedItem();
|
|
||||||
if(item && item->isApp()) {
|
|
||||||
char* mpath = menu_cache_dir_make_path(MENU_CACHE_DIR(item));
|
|
||||||
FmPath* path = fm_path_new_relative(fm_path_get_apps_menu(),
|
|
||||||
mpath + 13 /* skip "/Applications" */);
|
|
||||||
g_free(mpath);
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Fm
|
|
@ -1,73 +0,0 @@
|
|||||||
/*
|
|
||||||
* <one line to give the library's name and an idea of what it does.>
|
|
||||||
* Copyright (C) 2014 <copyright holder> <email>
|
|
||||||
*
|
|
||||||
* 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_APPMENUVIEW_H
|
|
||||||
#define FM_APPMENUVIEW_H
|
|
||||||
|
|
||||||
#include <QTreeView>
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
#include <menu-cache/menu-cache.h>
|
|
||||||
|
|
||||||
class QStandardItemModel;
|
|
||||||
class QStandardItem;
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class AppMenuViewItem;
|
|
||||||
|
|
||||||
class LIBFM_QT_API AppMenuView : public QTreeView {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit AppMenuView(QWidget* parent = NULL);
|
|
||||||
~AppMenuView();
|
|
||||||
|
|
||||||
GAppInfo* selectedApp();
|
|
||||||
|
|
||||||
const char* selectedAppDesktopId();
|
|
||||||
|
|
||||||
QByteArray selectedAppDesktopFilePath();
|
|
||||||
|
|
||||||
FmPath * selectedAppDesktopPath();
|
|
||||||
|
|
||||||
bool isAppSelected();
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void selectionChanged();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void addMenuItems(QStandardItem* parentItem, MenuCacheDir* dir);
|
|
||||||
void onMenuCacheReload(MenuCache* mc);
|
|
||||||
static void _onMenuCacheReload(MenuCache* mc, gpointer user_data) {
|
|
||||||
static_cast<AppMenuView*>(user_data)->onMenuCacheReload(mc);
|
|
||||||
}
|
|
||||||
|
|
||||||
AppMenuViewItem* selectedItem();
|
|
||||||
|
|
||||||
private:
|
|
||||||
// gboolean fm_app_menu_view_is_item_app(, GtkTreeIter* it);
|
|
||||||
QStandardItemModel* model_;
|
|
||||||
MenuCache* menu_cache;
|
|
||||||
MenuCacheNotifyId menu_cache_reload_notify;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_APPMENUVIEW_H
|
|
@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
* <one line to give the library's name and an idea of what it does.>
|
|
||||||
* Copyright (C) 2014 <copyright holder> <email>
|
|
||||||
*
|
|
||||||
* 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_APPMENUVIEW_P_H
|
|
||||||
#define FM_APPMENUVIEW_P_H
|
|
||||||
|
|
||||||
#include <QStandardItem>
|
|
||||||
#include <menu-cache/menu-cache.h>
|
|
||||||
#include "icontheme.h"
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class AppMenuViewItem : public QStandardItem {
|
|
||||||
public:
|
|
||||||
explicit AppMenuViewItem(MenuCacheItem* item):
|
|
||||||
item_(menu_cache_item_ref(item)) {
|
|
||||||
FmIcon* fmicon;
|
|
||||||
if(menu_cache_item_get_icon(item))
|
|
||||||
fmicon = fm_icon_from_name(menu_cache_item_get_icon(item));
|
|
||||||
else
|
|
||||||
fmicon = NULL;
|
|
||||||
setText(QString::fromUtf8(menu_cache_item_get_name(item)));
|
|
||||||
setEditable(false);
|
|
||||||
setDragEnabled(false);
|
|
||||||
if(fmicon) {
|
|
||||||
setIcon(IconTheme::icon(fmicon));
|
|
||||||
fm_icon_unref(fmicon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~AppMenuViewItem() {
|
|
||||||
menu_cache_item_unref(item_);
|
|
||||||
}
|
|
||||||
|
|
||||||
MenuCacheItem* item() {
|
|
||||||
return item_;
|
|
||||||
}
|
|
||||||
|
|
||||||
MenuCacheType type() {
|
|
||||||
return menu_cache_item_get_type(item_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isApp() {
|
|
||||||
return type() == MENU_CACHE_TYPE_APP;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isDir() {
|
|
||||||
return type() == MENU_CACHE_TYPE_DIR;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
MenuCacheItem* item_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_APPMENUVIEW_P_H
|
|
@ -1,30 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "bookmarkaction.h"
|
|
||||||
|
|
||||||
using namespace Fm;
|
|
||||||
|
|
||||||
BookmarkAction::BookmarkAction(FmBookmarkItem* item, QObject* parent):
|
|
||||||
QAction(parent),
|
|
||||||
item_(fm_bookmark_item_ref(item)) {
|
|
||||||
|
|
||||||
setText(QString::fromUtf8(item->name));
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef BOOKMARKACTION_H
|
|
||||||
#define BOOKMARKACTION_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QAction>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
// action used to create bookmark menu items
|
|
||||||
class LIBFM_QT_API BookmarkAction : public QAction {
|
|
||||||
public:
|
|
||||||
explicit BookmarkAction(FmBookmarkItem* item, QObject* parent = 0);
|
|
||||||
|
|
||||||
virtual ~BookmarkAction() {
|
|
||||||
if(item_)
|
|
||||||
fm_bookmark_item_unref(item_);
|
|
||||||
}
|
|
||||||
|
|
||||||
FmBookmarkItem* bookmark() {
|
|
||||||
return item_;
|
|
||||||
}
|
|
||||||
|
|
||||||
FmPath* path() {
|
|
||||||
return item_->path;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
FmBookmarkItem* item_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // BOOKMARKACTION_H
|
|
@ -1,87 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "browsehistory.h"
|
|
||||||
|
|
||||||
using namespace Fm;
|
|
||||||
|
|
||||||
BrowseHistory::BrowseHistory():
|
|
||||||
currentIndex_(0),
|
|
||||||
maxCount_(10) {
|
|
||||||
}
|
|
||||||
|
|
||||||
BrowseHistory::~BrowseHistory() {
|
|
||||||
}
|
|
||||||
|
|
||||||
void BrowseHistory::add(FmPath* path, int scrollPos) {
|
|
||||||
int lastIndex = size() - 1;
|
|
||||||
if(currentIndex_ < lastIndex) {
|
|
||||||
// if we're not at the last item, remove items after the current one.
|
|
||||||
erase(begin() + currentIndex_ + 1, end());
|
|
||||||
}
|
|
||||||
|
|
||||||
if(size() + 1 > maxCount_) {
|
|
||||||
// if there are too many items, remove the oldest one.
|
|
||||||
// FIXME: what if currentIndex_ == 0? remove the last item instead?
|
|
||||||
if(currentIndex_ == 0)
|
|
||||||
remove(lastIndex);
|
|
||||||
else {
|
|
||||||
remove(0);
|
|
||||||
--currentIndex_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// add a path and current scroll position to browse history
|
|
||||||
append(BrowseHistoryItem(path, scrollPos));
|
|
||||||
currentIndex_ = size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BrowseHistory::setCurrentIndex(int index) {
|
|
||||||
if(index >= 0 && index < size()) {
|
|
||||||
currentIndex_ = index;
|
|
||||||
// FIXME: should we emit a signal for the change?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BrowseHistory::canBackward() const {
|
|
||||||
return (currentIndex_ > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int BrowseHistory::backward() {
|
|
||||||
if(canBackward())
|
|
||||||
--currentIndex_;
|
|
||||||
return currentIndex_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BrowseHistory::canForward() const {
|
|
||||||
return (currentIndex_ + 1 < size());
|
|
||||||
}
|
|
||||||
|
|
||||||
int BrowseHistory::forward() {
|
|
||||||
if(canForward())
|
|
||||||
++currentIndex_;
|
|
||||||
return currentIndex_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BrowseHistory::setMaxCount(int maxCount) {
|
|
||||||
maxCount_ = maxCount;
|
|
||||||
if(size() > maxCount) {
|
|
||||||
// TODO: remove some items
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,131 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef FM_BROWSEHISTORY_H
|
|
||||||
#define FM_BROWSEHISTORY_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QVector>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
// class used to story browsing history of folder views
|
|
||||||
// We use this class to replace FmNavHistory provided by libfm since
|
|
||||||
// the original Libfm API is hard to use and confusing.
|
|
||||||
|
|
||||||
class LIBFM_QT_API BrowseHistoryItem {
|
|
||||||
public:
|
|
||||||
|
|
||||||
BrowseHistoryItem():
|
|
||||||
path_(NULL),
|
|
||||||
scrollPos_(0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
BrowseHistoryItem(FmPath* path, int scrollPos = 0):
|
|
||||||
path_(fm_path_ref(path)),
|
|
||||||
scrollPos_(scrollPos) {
|
|
||||||
}
|
|
||||||
|
|
||||||
BrowseHistoryItem(const BrowseHistoryItem& other):
|
|
||||||
path_(other.path_ ? fm_path_ref(other.path_) : NULL),
|
|
||||||
scrollPos_(other.scrollPos_) {
|
|
||||||
}
|
|
||||||
|
|
||||||
~BrowseHistoryItem() {
|
|
||||||
if(path_)
|
|
||||||
fm_path_unref(path_);
|
|
||||||
}
|
|
||||||
|
|
||||||
BrowseHistoryItem& operator=(const BrowseHistoryItem& other) {
|
|
||||||
if(path_)
|
|
||||||
fm_path_unref(path_);
|
|
||||||
path_ = other.path_ ? fm_path_ref(other.path_) : NULL;
|
|
||||||
scrollPos_ = other.scrollPos_;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
FmPath* path() const {
|
|
||||||
return path_;
|
|
||||||
}
|
|
||||||
|
|
||||||
int scrollPos() const {
|
|
||||||
return scrollPos_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setScrollPos(int pos) {
|
|
||||||
scrollPos_ = pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
FmPath* path_;
|
|
||||||
int scrollPos_;
|
|
||||||
// TODO: we may need to store current selection as well. reserve room for furutre expansion.
|
|
||||||
// void* reserved1;
|
|
||||||
// void* reserved2;
|
|
||||||
};
|
|
||||||
|
|
||||||
class LIBFM_QT_API BrowseHistory : public QVector<BrowseHistoryItem> {
|
|
||||||
|
|
||||||
public:
|
|
||||||
BrowseHistory();
|
|
||||||
virtual ~BrowseHistory();
|
|
||||||
|
|
||||||
int currentIndex() const {
|
|
||||||
return currentIndex_;
|
|
||||||
}
|
|
||||||
void setCurrentIndex(int index);
|
|
||||||
|
|
||||||
FmPath* currentPath() const {
|
|
||||||
return at(currentIndex_).path();
|
|
||||||
}
|
|
||||||
|
|
||||||
int currentScrollPos() const {
|
|
||||||
return at(currentIndex_).scrollPos();
|
|
||||||
}
|
|
||||||
|
|
||||||
BrowseHistoryItem& currentItem() {
|
|
||||||
return operator[](currentIndex_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void add(FmPath* path, int scrollPos = 0);
|
|
||||||
|
|
||||||
bool canForward() const;
|
|
||||||
|
|
||||||
bool canBackward() const;
|
|
||||||
|
|
||||||
int backward();
|
|
||||||
|
|
||||||
int forward();
|
|
||||||
|
|
||||||
int maxCount() const {
|
|
||||||
return maxCount_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setMaxCount(int maxCount);
|
|
||||||
|
|
||||||
private:
|
|
||||||
int currentIndex_;
|
|
||||||
int maxCount_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_BROWSEHISTORY_H
|
|
@ -1,73 +0,0 @@
|
|||||||
/*
|
|
||||||
<one line to give the library's name and an idea of what it does.>
|
|
||||||
Copyright (C) 2013 <copyright holder> <email>
|
|
||||||
|
|
||||||
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 "cachedfoldermodel.h"
|
|
||||||
|
|
||||||
using namespace Fm;
|
|
||||||
|
|
||||||
static GQuark data_id = 0;
|
|
||||||
|
|
||||||
|
|
||||||
CachedFolderModel::CachedFolderModel(FmFolder* folder):
|
|
||||||
FolderModel(),
|
|
||||||
refCount(1) {
|
|
||||||
|
|
||||||
FolderModel::setFolder(folder);
|
|
||||||
}
|
|
||||||
|
|
||||||
CachedFolderModel::~CachedFolderModel() {
|
|
||||||
}
|
|
||||||
|
|
||||||
CachedFolderModel* CachedFolderModel::modelFromFolder(FmFolder* folder) {
|
|
||||||
CachedFolderModel* model = NULL;
|
|
||||||
if(!data_id)
|
|
||||||
data_id = g_quark_from_static_string("CachedFolderModel");
|
|
||||||
gpointer qdata = g_object_get_qdata(G_OBJECT(folder), data_id);
|
|
||||||
model = reinterpret_cast<CachedFolderModel*>(qdata);
|
|
||||||
if(model) {
|
|
||||||
// qDebug("cache found!!");
|
|
||||||
model->ref();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
model = new CachedFolderModel(folder);
|
|
||||||
g_object_set_qdata(G_OBJECT(folder), data_id, model);
|
|
||||||
}
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
CachedFolderModel* CachedFolderModel::modelFromPath(FmPath* path) {
|
|
||||||
FmFolder* folder = fm_folder_from_path(path);
|
|
||||||
if(folder) {
|
|
||||||
CachedFolderModel* model = modelFromFolder(folder);
|
|
||||||
g_object_unref(folder);
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CachedFolderModel::unref() {
|
|
||||||
// qDebug("unref cache");
|
|
||||||
--refCount;
|
|
||||||
if(refCount <= 0) {
|
|
||||||
g_object_set_qdata(G_OBJECT(folder()), data_id, NULL);
|
|
||||||
deleteLater();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
|||||||
/*
|
|
||||||
<one line to give the library's name and an idea of what it does.>
|
|
||||||
Copyright (C) 2013 <copyright holder> <email>
|
|
||||||
|
|
||||||
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_CACHEDFOLDERMODEL_H
|
|
||||||
#define FM_CACHEDFOLDERMODEL_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include "foldermodel.h"
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class LIBFM_QT_API CachedFolderModel : public FolderModel {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
CachedFolderModel(FmFolder* folder);
|
|
||||||
void ref() {
|
|
||||||
++refCount;
|
|
||||||
}
|
|
||||||
void unref();
|
|
||||||
|
|
||||||
static CachedFolderModel* modelFromFolder(FmFolder* folder);
|
|
||||||
static CachedFolderModel* modelFromPath(FmPath* path);
|
|
||||||
|
|
||||||
private:
|
|
||||||
virtual ~CachedFolderModel();
|
|
||||||
void setFolder(FmFolder* folder);
|
|
||||||
private:
|
|
||||||
int refCount;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_CACHEDFOLDERMODEL_H
|
|
@ -1,51 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "colorbutton.h"
|
|
||||||
#include <QColorDialog>
|
|
||||||
|
|
||||||
using namespace Fm;
|
|
||||||
|
|
||||||
ColorButton::ColorButton(QWidget* parent): QPushButton(parent) {
|
|
||||||
connect(this, &QPushButton::clicked, this, &ColorButton::onClicked);
|
|
||||||
}
|
|
||||||
|
|
||||||
ColorButton::~ColorButton() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void ColorButton::onClicked() {
|
|
||||||
QColorDialog dlg(color_);
|
|
||||||
if(dlg.exec() == QDialog::Accepted) {
|
|
||||||
setColor(dlg.selectedColor());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ColorButton::setColor(const QColor& color) {
|
|
||||||
if(color != color_) {
|
|
||||||
color_ = color;
|
|
||||||
// use qss instead of QPalette to set the background color
|
|
||||||
// otherwise, this won't work when using the gtk style.
|
|
||||||
QString style = QString("QPushButton{background-color:%1;}").arg(color.name());
|
|
||||||
setStyleSheet(style);
|
|
||||||
Q_EMIT changed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef FM_COLORBUTTON_H
|
|
||||||
#define FM_COLORBUTTON_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QColor>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class LIBFM_QT_API ColorButton : public QPushButton {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit ColorButton(QWidget* parent = 0);
|
|
||||||
virtual ~ColorButton();
|
|
||||||
|
|
||||||
void setColor(const QColor&);
|
|
||||||
|
|
||||||
QColor color() const {
|
|
||||||
return color_;
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void changed();
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void onClicked();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QColor color_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_COLORBUTTON_H
|
|
@ -1,90 +0,0 @@
|
|||||||
/*
|
|
||||||
Menu with entries to create new folders and files.
|
|
||||||
Copyright (C) 2012 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 "createnewmenu.h"
|
|
||||||
#include "folderview.h"
|
|
||||||
#include "icontheme.h"
|
|
||||||
#include "utilities.h"
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
CreateNewMenu::CreateNewMenu(QWidget* dialogParent, FmPath* dirPath, QWidget* parent):
|
|
||||||
QMenu(parent), dialogParent_(dialogParent), dirPath_(dirPath) {
|
|
||||||
QAction* action = new QAction(QIcon::fromTheme("folder-new"), tr("Folder"), this);
|
|
||||||
connect(action, &QAction::triggered, this, &CreateNewMenu::onCreateNewFolder);
|
|
||||||
addAction(action);
|
|
||||||
|
|
||||||
action = new QAction(QIcon::fromTheme("document-new"), tr("Blank File"), this);
|
|
||||||
connect(action, &QAction::triggered, this, &CreateNewMenu::onCreateNewFile);
|
|
||||||
addAction(action);
|
|
||||||
|
|
||||||
// add more items to "Create New" menu from templates
|
|
||||||
GList* templates = fm_template_list_all(fm_config->only_user_templates);
|
|
||||||
if(templates) {
|
|
||||||
addSeparator();
|
|
||||||
for(GList* l = templates; l; l = l->next) {
|
|
||||||
FmTemplate* templ = (FmTemplate*)l->data;
|
|
||||||
/* we support directories differently */
|
|
||||||
if(fm_template_is_directory(templ))
|
|
||||||
continue;
|
|
||||||
FmMimeType* mime_type = fm_template_get_mime_type(templ);
|
|
||||||
const char* label = fm_template_get_label(templ);
|
|
||||||
QString text = QString("%1 (%2)").arg(QString::fromUtf8(label)).arg(QString::fromUtf8(fm_mime_type_get_desc(mime_type)));
|
|
||||||
FmIcon* icon = fm_template_get_icon(templ);
|
|
||||||
if(!icon)
|
|
||||||
icon = fm_mime_type_get_icon(mime_type);
|
|
||||||
QAction* action = addAction(IconTheme::icon(icon), text);
|
|
||||||
action->setObjectName(QString::fromUtf8(fm_template_get_name(templ, NULL)));
|
|
||||||
connect(action, &QAction::triggered, this, &CreateNewMenu::onCreateNew);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CreateNewMenu::~CreateNewMenu() {
|
|
||||||
}
|
|
||||||
|
|
||||||
void CreateNewMenu::onCreateNewFile() {
|
|
||||||
if(dirPath_)
|
|
||||||
createFileOrFolder(CreateNewTextFile, dirPath_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CreateNewMenu::onCreateNewFolder() {
|
|
||||||
if(dirPath_)
|
|
||||||
createFileOrFolder(CreateNewFolder, dirPath_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CreateNewMenu::onCreateNew() {
|
|
||||||
QAction* action = static_cast<QAction*>(sender());
|
|
||||||
QByteArray name = action->objectName().toUtf8();
|
|
||||||
GList* templates = fm_template_list_all(fm_config->only_user_templates);
|
|
||||||
FmTemplate* templ = NULL;
|
|
||||||
for(GList* l = templates; l; l = l->next) {
|
|
||||||
FmTemplate* t = (FmTemplate*)l->data;
|
|
||||||
if(name == fm_template_get_name(t, NULL)) {
|
|
||||||
templ = t;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(templ) { // template found
|
|
||||||
if(dirPath_)
|
|
||||||
createFileOrFolder(CreateWithTemplate, dirPath_, templ, dialogParent_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Fm
|
|
@ -1,51 +0,0 @@
|
|||||||
/*
|
|
||||||
Menu with entries to create new folders and files.
|
|
||||||
Copyright (C) 2012 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_CREATENEWMENU_H
|
|
||||||
#define FM_CREATENEWMENU_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QMenu>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class FolderView;
|
|
||||||
|
|
||||||
class LIBFM_QT_API CreateNewMenu : public QMenu {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit CreateNewMenu(QWidget* dialogParent, FmPath* dirPath,
|
|
||||||
QWidget* parent = 0);
|
|
||||||
virtual ~CreateNewMenu();
|
|
||||||
|
|
||||||
protected Q_SLOTS:
|
|
||||||
void onCreateNewFolder();
|
|
||||||
void onCreateNewFile();
|
|
||||||
void onCreateNew();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QWidget* dialogParent_;
|
|
||||||
FmPath* dirPath_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_CREATENEWMENU_H
|
|
@ -1,205 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2014 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dirtreemodel.h"
|
|
||||||
#include "dirtreemodelitem.h"
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
DirTreeModel::DirTreeModel(QObject* parent):
|
|
||||||
showHidden_(false) {
|
|
||||||
}
|
|
||||||
|
|
||||||
DirTreeModel::~DirTreeModel() {
|
|
||||||
}
|
|
||||||
|
|
||||||
// QAbstractItemModel implementation
|
|
||||||
|
|
||||||
Qt::ItemFlags DirTreeModel::flags(const QModelIndex& index) const {
|
|
||||||
DirTreeModelItem* item = itemFromIndex(index);
|
|
||||||
if(item && item->isPlaceHolder())
|
|
||||||
return Qt::ItemIsEnabled;
|
|
||||||
return QAbstractItemModel::flags(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant DirTreeModel::data(const QModelIndex& index, int role) const {
|
|
||||||
if(!index.isValid() || index.column() > 1) {
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
DirTreeModelItem* item = itemFromIndex(index);
|
|
||||||
if(item) {
|
|
||||||
FmFileInfo* info = item->fileInfo_;
|
|
||||||
switch(role) {
|
|
||||||
case Qt::ToolTipRole:
|
|
||||||
return QVariant(item->displayName_);
|
|
||||||
case Qt::DisplayRole:
|
|
||||||
return QVariant(item->displayName_);
|
|
||||||
case Qt::DecorationRole:
|
|
||||||
return QVariant(item->icon_);
|
|
||||||
case FileInfoRole:
|
|
||||||
return qVariantFromValue((void*)info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
int DirTreeModel::columnCount(const QModelIndex& parent) const {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int DirTreeModel::rowCount(const QModelIndex& parent) const {
|
|
||||||
if(!parent.isValid())
|
|
||||||
return rootItems_.count();
|
|
||||||
DirTreeModelItem* item = itemFromIndex(parent);
|
|
||||||
if(item)
|
|
||||||
return item->children_.count();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex DirTreeModel::parent(const QModelIndex& child) const {
|
|
||||||
DirTreeModelItem* item = itemFromIndex(child);
|
|
||||||
if(item && item->parent_) {
|
|
||||||
item = item->parent_; // go to parent item
|
|
||||||
if(item) {
|
|
||||||
const QList<DirTreeModelItem*>& items = item->parent_ ? item->parent_->children_ : rootItems_;
|
|
||||||
int row = items.indexOf(item); // this is Q(n) and may be slow :-(
|
|
||||||
if(row >= 0)
|
|
||||||
return createIndex(row, 0, (void*)item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return QModelIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex DirTreeModel::index(int row, int column, const QModelIndex& parent) const {
|
|
||||||
if(row >= 0 && column >= 0 && column == 0) {
|
|
||||||
if(!parent.isValid()) { // root items
|
|
||||||
if(row < rootItems_.count()) {
|
|
||||||
const DirTreeModelItem* item = rootItems_.at(row);
|
|
||||||
return createIndex(row, column, (void*)item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else { // child items
|
|
||||||
DirTreeModelItem* parentItem = itemFromIndex(parent);
|
|
||||||
if(row < parentItem->children_.count()) {
|
|
||||||
const DirTreeModelItem* item = parentItem->children_.at(row);
|
|
||||||
return createIndex(row, column, (void*)item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return QModelIndex(); // invalid index
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DirTreeModel::hasChildren(const QModelIndex& parent) const {
|
|
||||||
DirTreeModelItem* item = itemFromIndex(parent);
|
|
||||||
return item ? !item->isPlaceHolder() : true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex DirTreeModel::indexFromItem(DirTreeModelItem* item) const {
|
|
||||||
Q_ASSERT(item);
|
|
||||||
const QList<DirTreeModelItem*>& items = item->parent_ ? item->parent_->children_ : rootItems_;
|
|
||||||
int row = items.indexOf(item);
|
|
||||||
if(row >= 0)
|
|
||||||
return createIndex(row, 0, (void*)item);
|
|
||||||
return QModelIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
// public APIs
|
|
||||||
QModelIndex DirTreeModel::addRoot(FmFileInfo* root) {
|
|
||||||
DirTreeModelItem* item = new DirTreeModelItem(root, this);
|
|
||||||
int row = rootItems_.count();
|
|
||||||
beginInsertRows(QModelIndex(), row, row);
|
|
||||||
item->fileInfo_ = fm_file_info_ref(root);
|
|
||||||
rootItems_.append(item);
|
|
||||||
// add_place_holder_child_item(model, item_l, NULL, FALSE);
|
|
||||||
endInsertRows();
|
|
||||||
return QModelIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
DirTreeModelItem* DirTreeModel::itemFromIndex(const QModelIndex& index) const {
|
|
||||||
return reinterpret_cast<DirTreeModelItem*>(index.internalPointer());
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex DirTreeModel::indexFromPath(FmPath* path) const {
|
|
||||||
DirTreeModelItem* item = itemFromPath(path);
|
|
||||||
return item ? item->index() : QModelIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
DirTreeModelItem* DirTreeModel::itemFromPath(FmPath* path) const {
|
|
||||||
Q_FOREACH(DirTreeModelItem* item, rootItems_) {
|
|
||||||
if(item->fileInfo_ && fm_path_equal(path, fm_file_info_get_path(item->fileInfo_))) {
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
DirTreeModelItem* child = item->childFromPath(path, true);
|
|
||||||
if(child)
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void DirTreeModel::loadRow(const QModelIndex& index) {
|
|
||||||
DirTreeModelItem* item = itemFromIndex(index);
|
|
||||||
Q_ASSERT(item);
|
|
||||||
if(item && !item->isPlaceHolder())
|
|
||||||
item->loadFolder();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirTreeModel::unloadRow(const QModelIndex& index) {
|
|
||||||
DirTreeModelItem* item = itemFromIndex(index);
|
|
||||||
if(item && !item->isPlaceHolder())
|
|
||||||
item->unloadFolder();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DirTreeModel::isLoaded(const QModelIndex& index) {
|
|
||||||
DirTreeModelItem* item = itemFromIndex(index);
|
|
||||||
return item ? item->loaded_ : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QIcon DirTreeModel::icon(const QModelIndex& index) {
|
|
||||||
DirTreeModelItem* item = itemFromIndex(index);
|
|
||||||
return item ? item->icon_ : QIcon();
|
|
||||||
}
|
|
||||||
|
|
||||||
FmFileInfo* DirTreeModel::fileInfo(const QModelIndex& index) {
|
|
||||||
DirTreeModelItem* item = itemFromIndex(index);
|
|
||||||
return item ? item->fileInfo_ : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
FmPath* DirTreeModel::filePath(const QModelIndex& index) {
|
|
||||||
DirTreeModelItem* item = itemFromIndex(index);
|
|
||||||
return item && item->fileInfo_ ? fm_file_info_get_path(item->fileInfo_) : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString DirTreeModel::dispName(const QModelIndex& index) {
|
|
||||||
DirTreeModelItem* item = itemFromIndex(index);
|
|
||||||
return item ? item->displayName_ : QString();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirTreeModel::setShowHidden(bool show_hidden) {
|
|
||||||
showHidden_ = show_hidden;
|
|
||||||
Q_FOREACH(DirTreeModelItem* item, rootItems_) {
|
|
||||||
item->setShowHidden(show_hidden);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace Fm
|
|
@ -1,90 +0,0 @@
|
|||||||
/*
|
|
||||||
* <one line to give the program's name and a brief idea of what it does.>
|
|
||||||
* Copyright (C) 2014 <copyright holder> <email>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FM_DIRTREEMODEL_H
|
|
||||||
#define FM_DIRTREEMODEL_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QModelIndex>
|
|
||||||
#include <QAbstractItemModel>
|
|
||||||
#include <QIcon>
|
|
||||||
#include <QList>
|
|
||||||
#include <QSharedPointer>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class DirTreeModelItem;
|
|
||||||
class DirTreeView;
|
|
||||||
|
|
||||||
class LIBFM_QT_API DirTreeModel : public QAbstractItemModel {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
friend class DirTreeModelItem; // allow direct access of private members in DirTreeModelItem
|
|
||||||
friend class DirTreeView; // allow direct access of private members in DirTreeView
|
|
||||||
|
|
||||||
enum Role {
|
|
||||||
FileInfoRole = Qt::UserRole
|
|
||||||
};
|
|
||||||
|
|
||||||
explicit DirTreeModel(QObject* parent);
|
|
||||||
~DirTreeModel();
|
|
||||||
|
|
||||||
QModelIndex addRoot(FmFileInfo* root);
|
|
||||||
void loadRow(const QModelIndex& index);
|
|
||||||
void unloadRow(const QModelIndex& index);
|
|
||||||
|
|
||||||
bool isLoaded(const QModelIndex& index);
|
|
||||||
QIcon icon(const QModelIndex& index);
|
|
||||||
FmFileInfo* fileInfo(const QModelIndex& index);
|
|
||||||
FmPath* filePath(const QModelIndex& index);
|
|
||||||
QString dispName(const QModelIndex& index);
|
|
||||||
|
|
||||||
void setShowHidden(bool show_hidden);
|
|
||||||
bool showHidden() const {
|
|
||||||
return showHidden_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex indexFromPath(FmPath* path) const;
|
|
||||||
|
|
||||||
virtual Qt::ItemFlags flags(const QModelIndex& index) const;
|
|
||||||
virtual QVariant data(const QModelIndex& index, int role) const;
|
|
||||||
virtual int columnCount(const QModelIndex& parent) const;
|
|
||||||
virtual int rowCount(const QModelIndex& parent) const;
|
|
||||||
virtual QModelIndex parent(const QModelIndex& child) const;
|
|
||||||
virtual QModelIndex index(int row, int column, const QModelIndex& parent) const;
|
|
||||||
virtual bool hasChildren(const QModelIndex& parent = QModelIndex()) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
DirTreeModelItem* itemFromPath(FmPath* path) const;
|
|
||||||
DirTreeModelItem* itemFromIndex(const QModelIndex& index) const;
|
|
||||||
QModelIndex indexFromItem(DirTreeModelItem* item) const;
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void rowLoaded(const QModelIndex& index);
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool showHidden_;
|
|
||||||
QList<DirTreeModelItem*> rootItems_;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_DIRTREEMODEL_H
|
|
@ -1,339 +0,0 @@
|
|||||||
/*
|
|
||||||
* <one line to give the program's name and a brief idea of what it does.>
|
|
||||||
* Copyright (C) 2014 <copyright holder> <email>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dirtreemodelitem.h"
|
|
||||||
#include "dirtreemodel.h"
|
|
||||||
#include "icontheme.h"
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
DirTreeModelItem::DirTreeModelItem():
|
|
||||||
model_(NULL),
|
|
||||||
folder_(NULL),
|
|
||||||
expanded_(false),
|
|
||||||
loaded_(false),
|
|
||||||
fileInfo_(NULL),
|
|
||||||
placeHolderChild_(NULL),
|
|
||||||
parent_(NULL) {
|
|
||||||
}
|
|
||||||
|
|
||||||
DirTreeModelItem::DirTreeModelItem(FmFileInfo* info, DirTreeModel* model, DirTreeModelItem* parent):
|
|
||||||
model_(model),
|
|
||||||
folder_(NULL),
|
|
||||||
expanded_(false),
|
|
||||||
loaded_(false),
|
|
||||||
fileInfo_(fm_file_info_ref(info)),
|
|
||||||
displayName_(QString::fromUtf8(fm_file_info_get_disp_name(info))),
|
|
||||||
icon_(IconTheme::icon(fm_file_info_get_icon(info))),
|
|
||||||
placeHolderChild_(NULL),
|
|
||||||
parent_(parent) {
|
|
||||||
|
|
||||||
if(info)
|
|
||||||
addPlaceHolderChild();
|
|
||||||
}
|
|
||||||
|
|
||||||
DirTreeModelItem::~DirTreeModelItem() {
|
|
||||||
if(fileInfo_)
|
|
||||||
fm_file_info_unref(fileInfo_);
|
|
||||||
|
|
||||||
if(folder_)
|
|
||||||
freeFolder();
|
|
||||||
|
|
||||||
// delete child items if needed
|
|
||||||
if(!children_.isEmpty()) {
|
|
||||||
Q_FOREACH(DirTreeModelItem* item, children_) {
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!hiddenChildren_.isEmpty()) {
|
|
||||||
Q_FOREACH(DirTreeModelItem* item, hiddenChildren_) {
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirTreeModelItem::addPlaceHolderChild() {
|
|
||||||
placeHolderChild_ = new DirTreeModelItem();
|
|
||||||
placeHolderChild_->parent_ = this;
|
|
||||||
placeHolderChild_->model_ = model_;
|
|
||||||
placeHolderChild_->displayName_ = DirTreeModel::tr("Loading...");
|
|
||||||
children_.append(placeHolderChild_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirTreeModelItem::freeFolder() {
|
|
||||||
if(folder_) {
|
|
||||||
g_signal_handlers_disconnect_by_func(folder_, gpointer(onFolderFinishLoading), this);
|
|
||||||
g_signal_handlers_disconnect_by_func(folder_, gpointer(onFolderFilesAdded), this);
|
|
||||||
g_signal_handlers_disconnect_by_func(folder_, gpointer(onFolderFilesRemoved), this);
|
|
||||||
g_signal_handlers_disconnect_by_func(folder_, gpointer(onFolderFilesChanged), this);
|
|
||||||
g_object_unref(folder_);
|
|
||||||
folder_ = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirTreeModelItem::loadFolder() {
|
|
||||||
if(!expanded_) {
|
|
||||||
/* dynamically load content of the folder. */
|
|
||||||
folder_ = fm_folder_from_path(fm_file_info_get_path(fileInfo_));
|
|
||||||
/* g_debug("fm_dir_tree_model_load_row()"); */
|
|
||||||
/* associate the data with loaded handler */
|
|
||||||
g_signal_connect(folder_, "finish-loading", G_CALLBACK(onFolderFinishLoading), this);
|
|
||||||
g_signal_connect(folder_, "files-added", G_CALLBACK(onFolderFilesAdded), this);
|
|
||||||
g_signal_connect(folder_, "files-removed", G_CALLBACK(onFolderFilesRemoved), this);
|
|
||||||
g_signal_connect(folder_, "files-changed", G_CALLBACK(onFolderFilesChanged), this);
|
|
||||||
|
|
||||||
/* set 'expanded' flag beforehand as callback may check it */
|
|
||||||
expanded_ = true;
|
|
||||||
/* if the folder is already loaded, call "loaded" handler ourselves */
|
|
||||||
if(fm_folder_is_loaded(folder_)) { // already loaded
|
|
||||||
GList* file_l;
|
|
||||||
FmFileInfoList* files = fm_folder_get_files(folder_);
|
|
||||||
for(file_l = fm_file_info_list_peek_head_link(files); file_l; file_l = file_l->next) {
|
|
||||||
FmFileInfo* fi = FM_FILE_INFO(file_l->data);
|
|
||||||
if(fm_file_info_is_dir(fi)) {
|
|
||||||
insertFileInfo(fi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onFolderFinishLoading(folder_, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirTreeModelItem::unloadFolder() {
|
|
||||||
if(expanded_) { /* do some cleanup */
|
|
||||||
/* remove all children, and replace them with a dummy child
|
|
||||||
* item to keep expander in the tree view around. */
|
|
||||||
|
|
||||||
// delete all visible child items
|
|
||||||
model_->beginRemoveRows(index(), 0, children_.count() - 1);
|
|
||||||
if(!children_.isEmpty()) {
|
|
||||||
Q_FOREACH(DirTreeModelItem* item, children_) {
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
children_.clear();
|
|
||||||
}
|
|
||||||
model_->endRemoveRows();
|
|
||||||
|
|
||||||
// remove hidden children
|
|
||||||
if(!hiddenChildren_.isEmpty()) {
|
|
||||||
Q_FOREACH(DirTreeModelItem* item, hiddenChildren_) {
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
hiddenChildren_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* now, we have no child since all child items are removed.
|
|
||||||
* So we add a place holder child item to keep the expander around. */
|
|
||||||
addPlaceHolderChild();
|
|
||||||
/* deactivate folder since it will be reactivated on expand */
|
|
||||||
freeFolder();
|
|
||||||
expanded_ = false;
|
|
||||||
loaded_ = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex DirTreeModelItem::index() {
|
|
||||||
Q_ASSERT(model_);
|
|
||||||
return model_->indexFromItem(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add file info to parent node to proper position.
|
|
||||||
* GtkTreePath tp is the tree path of parent node. */
|
|
||||||
DirTreeModelItem* DirTreeModelItem::insertFileInfo(FmFileInfo* fi) {
|
|
||||||
// qDebug() << "insertFileInfo: " << fm_file_info_get_disp_name(fi);
|
|
||||||
DirTreeModelItem* item = new DirTreeModelItem(fi, model_);
|
|
||||||
insertItem(item);
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find a good position to insert the new item
|
|
||||||
int DirTreeModelItem::insertItem(DirTreeModelItem* newItem) {
|
|
||||||
if(model_->showHidden() || !newItem->fileInfo_ || !fm_file_info_is_hidden(newItem->fileInfo_)) {
|
|
||||||
const char* new_key = fm_file_info_get_collate_key(newItem->fileInfo_);
|
|
||||||
int pos = 0;
|
|
||||||
QList<DirTreeModelItem*>::iterator it;
|
|
||||||
for(it = children_.begin(); it != children_.end(); ++it) {
|
|
||||||
DirTreeModelItem* child = *it;
|
|
||||||
if(G_UNLIKELY(!child->fileInfo_))
|
|
||||||
continue;
|
|
||||||
const char* key = fm_file_info_get_collate_key(child->fileInfo_);
|
|
||||||
if(strcmp(new_key, key) <= 0)
|
|
||||||
break;
|
|
||||||
++pos;
|
|
||||||
}
|
|
||||||
// inform the world that we're about to insert the item
|
|
||||||
model_->beginInsertRows(index(), pos, pos);
|
|
||||||
newItem->parent_ = this;
|
|
||||||
children_.insert(it, newItem);
|
|
||||||
model_->endInsertRows();
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
else { // hidden folder
|
|
||||||
hiddenChildren_.append(newItem);
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// FmFolder signal handlers
|
|
||||||
|
|
||||||
// static
|
|
||||||
void DirTreeModelItem::onFolderFinishLoading(FmFolder* folder, gpointer user_data) {
|
|
||||||
DirTreeModelItem* _this = (DirTreeModelItem*)user_data;
|
|
||||||
DirTreeModel* model = _this->model_;
|
|
||||||
/* set 'loaded' flag beforehand as callback may check it */
|
|
||||||
_this->loaded_ = true;
|
|
||||||
QModelIndex index = _this->index();
|
|
||||||
qDebug() << "folder loaded";
|
|
||||||
// remove the placeholder child if needed
|
|
||||||
if(_this->children_.count() == 1) { // we have no other child other than the place holder item, leave it
|
|
||||||
_this->placeHolderChild_->displayName_ = DirTreeModel::tr("<No sub folders>");
|
|
||||||
QModelIndex placeHolderIndex = _this->placeHolderChild_->index();
|
|
||||||
// qDebug() << "placeHolderIndex: "<<placeHolderIndex;
|
|
||||||
Q_EMIT model->dataChanged(placeHolderIndex, placeHolderIndex);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
int pos = _this->children_.indexOf(_this->placeHolderChild_);
|
|
||||||
model->beginRemoveRows(index, pos, pos);
|
|
||||||
_this->children_.removeAt(pos);
|
|
||||||
delete _this->placeHolderChild_;
|
|
||||||
model->endRemoveRows();
|
|
||||||
_this->placeHolderChild_ = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_EMIT model->rowLoaded(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
void DirTreeModelItem::onFolderFilesAdded(FmFolder* folder, GSList* files, gpointer user_data) {
|
|
||||||
GSList* l;
|
|
||||||
DirTreeModelItem* _this = (DirTreeModelItem*)user_data;
|
|
||||||
for(l = files; l; l = l->next) {
|
|
||||||
FmFileInfo* fi = FM_FILE_INFO(l->data);
|
|
||||||
if(fm_file_info_is_dir(fi)) { /* FIXME: maybe adding files can be allowed later */
|
|
||||||
/* Ideally FmFolder should not emit files-added signals for files that
|
|
||||||
* already exists. So there is no need to check for duplication here. */
|
|
||||||
_this->insertFileInfo(fi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
void DirTreeModelItem::onFolderFilesRemoved(FmFolder* folder, GSList* files, gpointer user_data) {
|
|
||||||
DirTreeModelItem* _this = (DirTreeModelItem*)user_data;
|
|
||||||
DirTreeModel* model = _this->model_;
|
|
||||||
|
|
||||||
for(GSList* l = files; l; l = l->next) {
|
|
||||||
FmFileInfo* fi = FM_FILE_INFO(l->data);
|
|
||||||
int pos;
|
|
||||||
DirTreeModelItem* child = _this->childFromName(fm_file_info_get_name(fi), &pos);
|
|
||||||
if(child) {
|
|
||||||
model->beginRemoveRows(_this->index(), pos, pos);
|
|
||||||
_this->children_.removeAt(pos);
|
|
||||||
delete child;
|
|
||||||
model->endRemoveRows();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
void DirTreeModelItem::onFolderFilesChanged(FmFolder* folder, GSList* files, gpointer user_data) {
|
|
||||||
DirTreeModelItem* _this = (DirTreeModelItem*)user_data;
|
|
||||||
DirTreeModel* model = _this->model_;
|
|
||||||
|
|
||||||
for(GSList* l = files; l; l = l->next) {
|
|
||||||
FmFileInfo* changedFile = FM_FILE_INFO(l->data);
|
|
||||||
int pos;
|
|
||||||
DirTreeModelItem* child = _this->childFromName(fm_file_info_get_name(changedFile), &pos);
|
|
||||||
if(child) {
|
|
||||||
QModelIndex childIndex = child->index();
|
|
||||||
Q_EMIT model->dataChanged(childIndex, childIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DirTreeModelItem* DirTreeModelItem::childFromName(const char* utf8_name, int* pos) {
|
|
||||||
int i = 0;
|
|
||||||
Q_FOREACH(DirTreeModelItem* item, children_) {
|
|
||||||
if(item->fileInfo_ && strcmp(fm_file_info_get_name(item->fileInfo_), utf8_name) == 0) {
|
|
||||||
if(pos)
|
|
||||||
*pos = i;
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
DirTreeModelItem* DirTreeModelItem::childFromPath(FmPath* path, bool recursive) const {
|
|
||||||
Q_ASSERT(path != NULL);
|
|
||||||
|
|
||||||
Q_FOREACH(DirTreeModelItem* item, children_) {
|
|
||||||
// if(item->fileInfo_)
|
|
||||||
// qDebug() << "child: " << QString::fromUtf8(fm_file_info_get_disp_name(item->fileInfo_));
|
|
||||||
if(item->fileInfo_ && fm_path_equal(fm_file_info_get_path(item->fileInfo_), path)) {
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
else if(recursive) {
|
|
||||||
DirTreeModelItem* child = item->childFromPath(path, true);
|
|
||||||
if(child)
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirTreeModelItem::setShowHidden(bool show) {
|
|
||||||
if(show) {
|
|
||||||
// move all hidden children to visible list
|
|
||||||
Q_FOREACH(DirTreeModelItem* item, hiddenChildren_) {
|
|
||||||
insertItem(item);
|
|
||||||
}
|
|
||||||
hiddenChildren_.clear();
|
|
||||||
}
|
|
||||||
else { // hide hidden folders
|
|
||||||
QModelIndex _index = index();
|
|
||||||
QList<DirTreeModelItem*>::iterator it, next;
|
|
||||||
int pos = 0;
|
|
||||||
for(it = children_.begin(); it != children_.end(); ++pos) {
|
|
||||||
DirTreeModelItem* item = *it;
|
|
||||||
next = it + 1;
|
|
||||||
if(item->fileInfo_) {
|
|
||||||
if(fm_file_info_is_hidden(item->fileInfo_)) { // hidden folder
|
|
||||||
// remove from the model and add to the hiddenChildren_ list
|
|
||||||
model_->beginRemoveRows(_index, pos, pos);
|
|
||||||
children_.erase(it);
|
|
||||||
hiddenChildren_.append(item);
|
|
||||||
model_->endRemoveRows();
|
|
||||||
}
|
|
||||||
else { // visible folder, recursively filter its children
|
|
||||||
item->setShowHidden(show);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
it = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace Fm
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
|||||||
/*
|
|
||||||
* <one line to give the program's name and a brief idea of what it does.>
|
|
||||||
* Copyright (C) 2014 <copyright holder> <email>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FM_DIRTREEMODELITEM_H
|
|
||||||
#define FM_DIRTREEMODELITEM_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
#include <QIcon>
|
|
||||||
#include <QList>
|
|
||||||
#include <QModelIndex>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class DirTreeModel;
|
|
||||||
class DirTreeView;
|
|
||||||
|
|
||||||
class LIBFM_QT_API DirTreeModelItem {
|
|
||||||
public:
|
|
||||||
friend class DirTreeModel; // allow direct access of private members in DirTreeModel
|
|
||||||
friend class DirTreeView; // allow direct access of private members in DirTreeView
|
|
||||||
|
|
||||||
explicit DirTreeModelItem();
|
|
||||||
explicit DirTreeModelItem(FmFileInfo* info, DirTreeModel* model, DirTreeModelItem* parent = NULL);
|
|
||||||
~DirTreeModelItem();
|
|
||||||
|
|
||||||
void loadFolder();
|
|
||||||
void unloadFolder();
|
|
||||||
|
|
||||||
bool isPlaceHolder() {
|
|
||||||
return (fileInfo_ == NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setShowHidden(bool show);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void freeFolder();
|
|
||||||
void addPlaceHolderChild();
|
|
||||||
DirTreeModelItem* childFromName(const char* utf8_name, int* pos);
|
|
||||||
DirTreeModelItem* childFromPath(FmPath* path, bool recursive) const;
|
|
||||||
|
|
||||||
DirTreeModelItem* insertFileInfo(FmFileInfo* fi);
|
|
||||||
int insertItem(Fm::DirTreeModelItem* newItem);
|
|
||||||
QModelIndex index();
|
|
||||||
|
|
||||||
static void onFolderFinishLoading(FmFolder* folder, gpointer user_data);
|
|
||||||
static void onFolderFilesAdded(FmFolder* folder, GSList* files, gpointer user_data);
|
|
||||||
static void onFolderFilesRemoved(FmFolder* folder, GSList* files, gpointer user_data);
|
|
||||||
static void onFolderFilesChanged(FmFolder* folder, GSList* files, gpointer user_data);
|
|
||||||
|
|
||||||
private:
|
|
||||||
FmFileInfo* fileInfo_;
|
|
||||||
FmFolder* folder_;
|
|
||||||
QString displayName_ ;
|
|
||||||
QIcon icon_;
|
|
||||||
bool expanded_;
|
|
||||||
bool loaded_;
|
|
||||||
DirTreeModelItem* parent_;
|
|
||||||
DirTreeModelItem* placeHolderChild_;
|
|
||||||
QList<DirTreeModelItem*> children_;
|
|
||||||
QList<DirTreeModelItem*> hiddenChildren_;
|
|
||||||
DirTreeModel* model_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_DIRTREEMODELITEM_H
|
|
@ -1,296 +0,0 @@
|
|||||||
/*
|
|
||||||
* <one line to give the program's name and a brief idea of what it does.>
|
|
||||||
* Copyright (C) 2014 <copyright holder> <email>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dirtreeview.h"
|
|
||||||
#include <QHeaderView>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QItemSelection>
|
|
||||||
#include <QGuiApplication>
|
|
||||||
#include <QMouseEvent>
|
|
||||||
#include "dirtreemodel.h"
|
|
||||||
#include "dirtreemodelitem.h"
|
|
||||||
#include "filemenu.h"
|
|
||||||
|
|
||||||
using namespace Fm;
|
|
||||||
|
|
||||||
DirTreeView::DirTreeView(QWidget* parent):
|
|
||||||
currentExpandingItem_(NULL),
|
|
||||||
currentPath_(NULL) {
|
|
||||||
|
|
||||||
setSelectionMode(QAbstractItemView::SingleSelection);
|
|
||||||
setHeaderHidden(true);
|
|
||||||
setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
|
|
||||||
header()->setStretchLastSection(false);
|
|
||||||
|
|
||||||
connect(this, &DirTreeView::collapsed, this, &DirTreeView::onCollapsed);
|
|
||||||
connect(this, &DirTreeView::expanded, this, &DirTreeView::onExpanded);
|
|
||||||
|
|
||||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
|
||||||
connect(this, &DirTreeView::customContextMenuRequested,
|
|
||||||
this, &DirTreeView::onCustomContextMenuRequested);
|
|
||||||
}
|
|
||||||
|
|
||||||
DirTreeView::~DirTreeView() {
|
|
||||||
if(currentPath_)
|
|
||||||
fm_path_unref(currentPath_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirTreeView::cancelPendingChdir() {
|
|
||||||
if(!pathsToExpand_.isEmpty()) {
|
|
||||||
pathsToExpand_.clear();
|
|
||||||
if(!currentExpandingItem_)
|
|
||||||
return;
|
|
||||||
DirTreeModel* _model = static_cast<DirTreeModel*>(model());
|
|
||||||
disconnect(_model, &DirTreeModel::rowLoaded, this, &DirTreeView::onRowLoaded);
|
|
||||||
currentExpandingItem_ = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirTreeView::expandPendingPath() {
|
|
||||||
if(pathsToExpand_.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
FmPath* path = pathsToExpand_.first().data();
|
|
||||||
// qDebug() << "expanding: " << Path(path).displayBasename();
|
|
||||||
DirTreeModel* _model = static_cast<DirTreeModel*>(model());
|
|
||||||
DirTreeModelItem* item = _model->itemFromPath(path);
|
|
||||||
// qDebug() << "findItem: " << item;
|
|
||||||
if(item) {
|
|
||||||
currentExpandingItem_ = item;
|
|
||||||
connect(_model, &DirTreeModel::rowLoaded, this, &DirTreeView::onRowLoaded);
|
|
||||||
if(item->loaded_) { // the node is already loaded
|
|
||||||
onRowLoaded(item->index());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// _model->loadRow(item->index());
|
|
||||||
item->loadFolder();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
selectionModel()->clear();
|
|
||||||
/* since we never get it loaded we need to update cwd here */
|
|
||||||
if(currentPath_)
|
|
||||||
fm_path_unref(currentPath_);
|
|
||||||
currentPath_ = fm_path_ref(path);
|
|
||||||
|
|
||||||
cancelPendingChdir(); // FIXME: is this correct? this is not done in the gtk+ version of libfm.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirTreeView::onRowLoaded(const QModelIndex& index) {
|
|
||||||
DirTreeModel* _model = static_cast<DirTreeModel*>(model());
|
|
||||||
if(!currentExpandingItem_)
|
|
||||||
return;
|
|
||||||
if(currentExpandingItem_ != _model->itemFromIndex(index)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* disconnect the handler since we only need it once */
|
|
||||||
disconnect(_model, &DirTreeModel::rowLoaded, this, &DirTreeView::onRowLoaded);
|
|
||||||
|
|
||||||
// DirTreeModelItem* item = _model->itemFromIndex(index);
|
|
||||||
// qDebug() << "row loaded: " << item->displayName_;
|
|
||||||
/* after the folder is loaded, the files should have been added to
|
|
||||||
* the tree model */
|
|
||||||
expand(index);
|
|
||||||
|
|
||||||
/* remove the expanded path from pending list */
|
|
||||||
pathsToExpand_.removeFirst();
|
|
||||||
if(pathsToExpand_.isEmpty()) { /* this is the last one and we're done, select the item */
|
|
||||||
// qDebug() << "Done!";
|
|
||||||
selectionModel()->select(index, QItemSelectionModel::SelectCurrent|QItemSelectionModel::Clear);
|
|
||||||
scrollTo(index, QAbstractItemView::EnsureVisible);
|
|
||||||
}
|
|
||||||
else { /* continue expanding next pending path */
|
|
||||||
expandPendingPath();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void DirTreeView::setCurrentPath(FmPath* path) {
|
|
||||||
DirTreeModel* _model = static_cast<DirTreeModel*>(model());
|
|
||||||
if(!_model)
|
|
||||||
return;
|
|
||||||
int rowCount = _model->rowCount(QModelIndex());
|
|
||||||
if(rowCount <= 0 || fm_path_equal(currentPath_, path))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(currentPath_)
|
|
||||||
fm_path_unref(currentPath_);
|
|
||||||
currentPath_ = fm_path_ref(path);
|
|
||||||
|
|
||||||
// NOTE: The content of each node is loaded on demand dynamically.
|
|
||||||
// So, when we ask for a chdir operation, some nodes do not exists yet.
|
|
||||||
// We have to wait for the loading of child nodes and continue the
|
|
||||||
// pending chdir operation after the child nodes become available.
|
|
||||||
|
|
||||||
// cancel previous pending tree expansion
|
|
||||||
cancelPendingChdir();
|
|
||||||
|
|
||||||
/* find a root item containing this path */
|
|
||||||
FmPath* root;
|
|
||||||
for(int row = 0; row < rowCount; ++row) {
|
|
||||||
QModelIndex index = _model->index(row, 0, QModelIndex());
|
|
||||||
root = _model->filePath(index);
|
|
||||||
if(fm_path_has_prefix(path, root))
|
|
||||||
break;
|
|
||||||
root = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(root) { /* root item is found */
|
|
||||||
do { /* add path elements one by one to a list */
|
|
||||||
pathsToExpand_.prepend(path);
|
|
||||||
// qDebug() << "prepend path: " << Path(path).displayBasename();
|
|
||||||
if(fm_path_equal(path, root))
|
|
||||||
break;
|
|
||||||
path = fm_path_get_parent(path);
|
|
||||||
}
|
|
||||||
while(path);
|
|
||||||
|
|
||||||
expandPendingPath();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirTreeView::setModel(QAbstractItemModel* model) {
|
|
||||||
Q_ASSERT(model->inherits("Fm::DirTreeModel"));
|
|
||||||
|
|
||||||
if(!pathsToExpand_.isEmpty()) // if a chdir request is in progress, cancel it
|
|
||||||
cancelPendingChdir();
|
|
||||||
|
|
||||||
QTreeView::setModel(model);
|
|
||||||
header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
|
|
||||||
connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, &DirTreeView::onSelectionChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirTreeView::mousePressEvent(QMouseEvent* event) {
|
|
||||||
if(event && event->button() == Qt::RightButton &&
|
|
||||||
event->type() == QEvent::MouseButtonPress) {
|
|
||||||
// Do not change the selection when the context menu is activated.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
QTreeView::mousePressEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirTreeView::onCustomContextMenuRequested(const QPoint& pos) {
|
|
||||||
QModelIndex index = indexAt(pos);
|
|
||||||
if(index.isValid()) {
|
|
||||||
QVariant data = index.data(DirTreeModel::FileInfoRole);
|
|
||||||
FmFileInfo* fileInfo = reinterpret_cast<FmFileInfo*>(data.value<void*>());
|
|
||||||
if(fileInfo) {
|
|
||||||
FmPath* path = fm_file_info_get_path(fileInfo);
|
|
||||||
FmFileInfoList* files = fm_file_info_list_new();
|
|
||||||
fm_file_info_list_push_tail(files, fileInfo);
|
|
||||||
Fm::FileMenu* menu = new Fm::FileMenu(files, fileInfo, path);
|
|
||||||
// FIXME: apply some settings to the menu and set a proper file launcher to it
|
|
||||||
Q_EMIT prepareFileMenu(menu);
|
|
||||||
fm_file_info_list_unref(files);
|
|
||||||
QVariant pathData = qVariantFromValue(reinterpret_cast<void*>(path));
|
|
||||||
QAction* action = menu->openAction();
|
|
||||||
action->disconnect();
|
|
||||||
action->setData(index);
|
|
||||||
connect(action, &QAction::triggered, this, &DirTreeView::onOpen);
|
|
||||||
action = new QAction(QIcon::fromTheme("window-new"), tr("Open in New T&ab"), menu);
|
|
||||||
action->setData(pathData);
|
|
||||||
connect(action, &QAction::triggered, this, &DirTreeView::onNewTab);
|
|
||||||
menu->insertAction(menu->separator1(), action);
|
|
||||||
action = new QAction(QIcon::fromTheme("window-new"), tr("Open in New Win&dow"), menu);
|
|
||||||
action->setData(pathData);
|
|
||||||
connect(action, &QAction::triggered, this, &DirTreeView::onNewWindow);
|
|
||||||
menu->insertAction(menu->separator1(), action);
|
|
||||||
if(fm_file_info_is_native(fileInfo)) {
|
|
||||||
action = new QAction(QIcon::fromTheme("utilities-terminal"), tr("Open in Termina&l"), menu);
|
|
||||||
action->setData(pathData);
|
|
||||||
connect(action, &QAction::triggered, this, &DirTreeView::onOpenInTerminal);
|
|
||||||
menu->insertAction(menu->separator1(), action);
|
|
||||||
}
|
|
||||||
menu->exec(mapToGlobal(pos));
|
|
||||||
delete menu;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirTreeView::onOpen() {
|
|
||||||
if(QAction* action = qobject_cast<QAction*>(sender())) {
|
|
||||||
setCurrentIndex(action->data().toModelIndex());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirTreeView::onNewWindow() {
|
|
||||||
if(QAction* action = qobject_cast<QAction*>(sender())) {
|
|
||||||
FmPath* path = reinterpret_cast<FmPath*>(action->data().value<void*>());
|
|
||||||
Q_EMIT openFolderInNewWindowRequested(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirTreeView::onNewTab() {
|
|
||||||
if(QAction* action = qobject_cast<QAction*>(sender())) {
|
|
||||||
FmPath* path = reinterpret_cast<FmPath*>(action->data().value<void*>());
|
|
||||||
Q_EMIT openFolderInNewTabRequested(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirTreeView::onOpenInTerminal() {
|
|
||||||
if(QAction* action = qobject_cast<QAction*>(sender())) {
|
|
||||||
FmPath* path = reinterpret_cast<FmPath*>(action->data().value<void*>());
|
|
||||||
Q_EMIT openFolderInTerminalRequested(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirTreeView::onNewFolder() {
|
|
||||||
if(QAction* action = qobject_cast<QAction*>(sender())) {
|
|
||||||
FmPath* path = reinterpret_cast<FmPath*>(action->data().value<void*>());
|
|
||||||
Q_EMIT createNewFolderRequested(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirTreeView::onCollapsed(const QModelIndex& index) {
|
|
||||||
DirTreeModel* treeModel = static_cast<DirTreeModel*>(model());
|
|
||||||
if(treeModel) {
|
|
||||||
treeModel->unloadRow(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirTreeView::onExpanded(const QModelIndex& index) {
|
|
||||||
DirTreeModel* treeModel = static_cast<DirTreeModel*>(model());
|
|
||||||
if(treeModel) {
|
|
||||||
treeModel->loadRow(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DirTreeView::onSelectionChanged(const QItemSelection & selected, const QItemSelection & deselected) {
|
|
||||||
if(!selected.isEmpty()) {
|
|
||||||
QModelIndex index = selected.first().topLeft();
|
|
||||||
DirTreeModel* _model = static_cast<DirTreeModel*>(model());
|
|
||||||
FmPath* path = _model->filePath(index);
|
|
||||||
if(path && currentPath_ && fm_path_equal(path, currentPath_))
|
|
||||||
return;
|
|
||||||
cancelPendingChdir();
|
|
||||||
if(!path)
|
|
||||||
return;
|
|
||||||
if(currentPath_)
|
|
||||||
fm_path_unref(currentPath_);
|
|
||||||
currentPath_ = fm_path_ref(path);
|
|
||||||
|
|
||||||
// FIXME: use enums for type rather than hard-coded values 0 or 1
|
|
||||||
int type = 0;
|
|
||||||
if(QGuiApplication::mouseButtons() & Qt::MiddleButton)
|
|
||||||
type = 1;
|
|
||||||
Q_EMIT chdirRequested(type, path);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
/*
|
|
||||||
* <one line to give the program's name and a brief idea of what it does.>
|
|
||||||
* Copyright (C) 2014 <copyright holder> <email>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FM_DIRTREEVIEW_H
|
|
||||||
#define FM_DIRTREEVIEW_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QTreeView>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
#include "path.h"
|
|
||||||
|
|
||||||
class QItemSelection;
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class FileMenu;
|
|
||||||
class DirTreeModelItem;
|
|
||||||
|
|
||||||
class LIBFM_QT_API DirTreeView : public QTreeView {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
DirTreeView(QWidget* parent);
|
|
||||||
~DirTreeView();
|
|
||||||
|
|
||||||
FmPath* currentPath() {
|
|
||||||
return currentPath_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setCurrentPath(FmPath* path);
|
|
||||||
|
|
||||||
// libfm-gtk compatible alias
|
|
||||||
FmPath* getCwd() {
|
|
||||||
return currentPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
void chdir(FmPath* path) {
|
|
||||||
setCurrentPath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void setModel(QAbstractItemModel* model);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void mousePressEvent(QMouseEvent* event);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void cancelPendingChdir();
|
|
||||||
void expandPendingPath();
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void chdirRequested(int type, FmPath* path);
|
|
||||||
void openFolderInNewWindowRequested(FmPath* path);
|
|
||||||
void openFolderInNewTabRequested(FmPath* path);
|
|
||||||
void openFolderInTerminalRequested(FmPath* path);
|
|
||||||
void createNewFolderRequested(FmPath* path);
|
|
||||||
void prepareFileMenu(Fm::FileMenu* menu); // emit before showing a Fm::FileMenu
|
|
||||||
|
|
||||||
protected Q_SLOTS:
|
|
||||||
void onCollapsed(const QModelIndex & index);
|
|
||||||
void onExpanded(const QModelIndex & index);
|
|
||||||
void onRowLoaded(const QModelIndex& index);
|
|
||||||
void onSelectionChanged(const QItemSelection & selected, const QItemSelection & deselected);
|
|
||||||
void onCustomContextMenuRequested(const QPoint& pos);
|
|
||||||
void onOpen();
|
|
||||||
void onNewWindow();
|
|
||||||
void onNewTab();
|
|
||||||
void onOpenInTerminal();
|
|
||||||
void onNewFolder();
|
|
||||||
|
|
||||||
private:
|
|
||||||
FmPath* currentPath_;
|
|
||||||
QList<Path> pathsToExpand_;
|
|
||||||
DirTreeModelItem* currentExpandingItem_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_DIRTREEVIEW_H
|
|
@ -1,50 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "dndactionmenu.h"
|
|
||||||
|
|
||||||
using namespace Fm;
|
|
||||||
|
|
||||||
DndActionMenu::DndActionMenu(QWidget* parent): QMenu(parent) {
|
|
||||||
copyAction = addAction(QIcon::fromTheme("edit-copy"), tr("Copy here"));
|
|
||||||
moveAction = addAction(tr("Move here"));
|
|
||||||
linkAction = addAction(tr("Create symlink here"));
|
|
||||||
addSeparator();
|
|
||||||
cancelAction = addAction(tr("Cancel"));
|
|
||||||
}
|
|
||||||
|
|
||||||
DndActionMenu::~DndActionMenu() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt::DropAction DndActionMenu::askUser(QPoint pos) {
|
|
||||||
Qt::DropAction result;
|
|
||||||
DndActionMenu menu;
|
|
||||||
QAction* action = menu.exec(pos);
|
|
||||||
if(action == menu.copyAction)
|
|
||||||
result = Qt::CopyAction;
|
|
||||||
else if(action == menu.moveAction)
|
|
||||||
result = Qt::MoveAction;
|
|
||||||
else if(action == menu.linkAction)
|
|
||||||
result = Qt::LinkAction;
|
|
||||||
else
|
|
||||||
result = Qt::IgnoreAction;
|
|
||||||
return result;
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef FM_DNDACTIONMENU_H
|
|
||||||
#define FM_DNDACTIONMENU_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QMenu>
|
|
||||||
#include <QAction>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class DndActionMenu : public QMenu {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit DndActionMenu(QWidget* parent = 0);
|
|
||||||
virtual ~DndActionMenu();
|
|
||||||
|
|
||||||
static Qt::DropAction askUser(QPoint pos);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QAction* copyAction;
|
|
||||||
QAction* moveAction;
|
|
||||||
QAction* linkAction;
|
|
||||||
QAction* cancelAction;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_DNDACTIONMENU_H
|
|
@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
* <one line to give the program's name and a brief idea of what it does.>
|
|
||||||
* Copyright (C) 2014 <copyright holder> <email>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "dnddest.h"
|
|
||||||
#include "fileoperation.h"
|
|
||||||
#include "utilities.h"
|
|
||||||
|
|
||||||
using namespace Fm;
|
|
||||||
|
|
||||||
const char* supportedMimeTypes[] = {
|
|
||||||
"text/uri-list"
|
|
||||||
"XdndDirectSave0"/* X direct save */
|
|
||||||
/* TODO: add more targets to support: text types, _NETSCAPE_URL, property/bgimage ... */
|
|
||||||
};
|
|
||||||
|
|
||||||
DndDest::DndDest() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
DndDest::~DndDest() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DndDest::dropMimeData(const QMimeData* data, Qt::DropAction action) {
|
|
||||||
// FIXME: should we put this in dropEvent handler of FolderView instead?
|
|
||||||
if(data->hasUrls()) {
|
|
||||||
qDebug("drop action: %d", action);
|
|
||||||
FmPathList* srcPaths = pathListFromQUrls(data->urls());
|
|
||||||
switch(action) {
|
|
||||||
case Qt::CopyAction:
|
|
||||||
FileOperation::copyFiles(srcPaths, destPath_.data());
|
|
||||||
break;
|
|
||||||
case Qt::MoveAction:
|
|
||||||
FileOperation::moveFiles(srcPaths, destPath_.data());
|
|
||||||
break;
|
|
||||||
case Qt::LinkAction:
|
|
||||||
FileOperation::symlinkFiles(srcPaths, destPath_.data());
|
|
||||||
default:
|
|
||||||
fm_path_list_unref(srcPaths);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
fm_path_list_unref(srcPaths);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DndDest::isSupported(const QMimeData* data) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DndDest::isSupported(QString mimeType) {
|
|
||||||
return false;
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
* <one line to give the program's name and a brief idea of what it does.>
|
|
||||||
* Copyright (C) 2014 <copyright holder> <email>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FM_DNDDEST_H
|
|
||||||
#define FM_DNDDEST_H
|
|
||||||
|
|
||||||
#include <QMimeData>
|
|
||||||
#include "path.h"
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class DndDest {
|
|
||||||
public:
|
|
||||||
DndDest();
|
|
||||||
~DndDest();
|
|
||||||
|
|
||||||
void setDestPath(FmPath* dest) {
|
|
||||||
destPath_ = dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Path& destPath() {
|
|
||||||
return destPath_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isSupported(const QMimeData* data);
|
|
||||||
bool isSupported(QString mimeType);
|
|
||||||
|
|
||||||
bool dropMimeData(const QMimeData* data, Qt::DropAction action);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Path destPath_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_DNDDEST_H
|
|
@ -1,143 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>EditBookmarksDialog</class>
|
|
||||||
<widget class="QDialog" name="EditBookmarksDialog">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>480</width>
|
|
||||||
<height>320</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Edit Bookmarks</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QTreeWidget" name="treeWidget">
|
|
||||||
<property name="acceptDrops">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="dragEnabled">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="dragDropMode">
|
|
||||||
<enum>QAbstractItemView::InternalMove</enum>
|
|
||||||
</property>
|
|
||||||
<property name="defaultDropAction">
|
|
||||||
<enum>Qt::MoveAction</enum>
|
|
||||||
</property>
|
|
||||||
<property name="rootIsDecorated">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="itemsExpandable">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<attribute name="headerDefaultSectionSize">
|
|
||||||
<number>100</number>
|
|
||||||
</attribute>
|
|
||||||
<column>
|
|
||||||
<property name="text">
|
|
||||||
<string>Name</string>
|
|
||||||
</property>
|
|
||||||
</column>
|
|
||||||
<column>
|
|
||||||
<property name="text">
|
|
||||||
<string>Location</string>
|
|
||||||
</property>
|
|
||||||
</column>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0" colspan="2">
|
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="standardButtons">
|
|
||||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="addItem">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Add Item</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="list-add"/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="removeItem">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Remove Item</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="list-remove"/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="verticalSpacer">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>40</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="text">
|
|
||||||
<string>Use drag and drop to reorder the items</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections>
|
|
||||||
<connection>
|
|
||||||
<sender>buttonBox</sender>
|
|
||||||
<signal>accepted()</signal>
|
|
||||||
<receiver>EditBookmarksDialog</receiver>
|
|
||||||
<slot>accept()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>248</x>
|
|
||||||
<y>254</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>157</x>
|
|
||||||
<y>274</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
<connection>
|
|
||||||
<sender>buttonBox</sender>
|
|
||||||
<signal>rejected()</signal>
|
|
||||||
<receiver>EditBookmarksDialog</receiver>
|
|
||||||
<slot>reject()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>316</x>
|
|
||||||
<y>260</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>286</x>
|
|
||||||
<y>274</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
</connections>
|
|
||||||
</ui>
|
|
@ -1,109 +0,0 @@
|
|||||||
/*
|
|
||||||
<one line to give the program's name and a brief idea of what it does.>
|
|
||||||
Copyright (C) 2013 PCMan <email>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "editbookmarksdialog.h"
|
|
||||||
#include "ui_edit-bookmarks.h"
|
|
||||||
#include <QByteArray>
|
|
||||||
#include <QUrl>
|
|
||||||
#include <QSaveFile>
|
|
||||||
#include <QStandardPaths>
|
|
||||||
#include <QDir>
|
|
||||||
|
|
||||||
using namespace Fm;
|
|
||||||
|
|
||||||
EditBookmarksDialog::EditBookmarksDialog(FmBookmarks* bookmarks, QWidget* parent, Qt::WindowFlags f):
|
|
||||||
QDialog(parent, f),
|
|
||||||
ui(new Ui::EditBookmarksDialog()),
|
|
||||||
bookmarks_(FM_BOOKMARKS(g_object_ref(bookmarks))) {
|
|
||||||
|
|
||||||
ui->setupUi(this);
|
|
||||||
setAttribute(Qt::WA_DeleteOnClose); // auto delete on close
|
|
||||||
|
|
||||||
// load bookmarks
|
|
||||||
GList* allBookmarks = fm_bookmarks_get_all(bookmarks_);
|
|
||||||
for(GList* l = allBookmarks; l; l = l->next) {
|
|
||||||
FmBookmarkItem* bookmark = reinterpret_cast<FmBookmarkItem*>(l->data);
|
|
||||||
QTreeWidgetItem* item = new QTreeWidgetItem();
|
|
||||||
char* path_str = fm_path_display_name(bookmark->path, false);
|
|
||||||
item->setData(0, Qt::DisplayRole, QString::fromUtf8(bookmark->name));
|
|
||||||
item->setData(1, Qt::DisplayRole, QString::fromUtf8(path_str));
|
|
||||||
item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEditable|Qt::ItemIsDragEnabled|Qt::ItemIsEnabled);
|
|
||||||
g_free(path_str);
|
|
||||||
ui->treeWidget->addTopLevelItem(item);
|
|
||||||
}
|
|
||||||
g_list_free_full(allBookmarks, (GDestroyNotify)fm_bookmark_item_unref);
|
|
||||||
|
|
||||||
connect(ui->addItem, &QPushButton::clicked, this, &EditBookmarksDialog::onAddItem);
|
|
||||||
connect(ui->removeItem, &QPushButton::clicked, this, &EditBookmarksDialog::onRemoveItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
EditBookmarksDialog::~EditBookmarksDialog() {
|
|
||||||
g_object_unref(bookmarks_);
|
|
||||||
delete ui;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditBookmarksDialog::accept() {
|
|
||||||
// save bookmarks
|
|
||||||
// it's easier to recreate the whole bookmark file than
|
|
||||||
// to manipulate FmBookmarks object. So here we generate the file directly.
|
|
||||||
// FIXME: maybe in the future we should add a libfm API to easily replace all FmBookmarks.
|
|
||||||
// Here we use gtk+ 3.0 bookmarks rather than the gtk+ 2.0 one.
|
|
||||||
// Since gtk+ 2.24.12, gtk+2 reads gtk+3 bookmarks file if it exists.
|
|
||||||
// So it's safe to only save gtk+3 bookmarks file.
|
|
||||||
QString path = QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
|
|
||||||
path += QLatin1String("/gtk-3.0");
|
|
||||||
if(!QDir().mkpath(path))
|
|
||||||
return; // fail to create ~/.config/gtk-3.0 dir
|
|
||||||
path += QLatin1String("/bookmarks");
|
|
||||||
QSaveFile file(path); // use QSaveFile for atomic file operation
|
|
||||||
if(file.open(QIODevice::WriteOnly)){
|
|
||||||
for(int row = 0; ; ++row) {
|
|
||||||
QTreeWidgetItem* item = ui->treeWidget->topLevelItem(row);
|
|
||||||
if(!item)
|
|
||||||
break;
|
|
||||||
QString name = item->data(0, Qt::DisplayRole).toString();
|
|
||||||
QUrl url = QUrl::fromUserInput(item->data(1, Qt::DisplayRole).toString());
|
|
||||||
file.write(url.toEncoded());
|
|
||||||
file.write(" ");
|
|
||||||
file.write(name.toUtf8());
|
|
||||||
file.write("\n");
|
|
||||||
}
|
|
||||||
// FIXME: should we support Qt or KDE specific bookmarks in the future?
|
|
||||||
file.commit();
|
|
||||||
}
|
|
||||||
QDialog::accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditBookmarksDialog::onAddItem() {
|
|
||||||
QTreeWidgetItem* item = new QTreeWidgetItem();
|
|
||||||
item->setData(0, Qt::DisplayRole, tr("New bookmark"));
|
|
||||||
item->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEditable|Qt::ItemIsDragEnabled|Qt::ItemIsEnabled);
|
|
||||||
ui->treeWidget->addTopLevelItem(item);
|
|
||||||
ui->treeWidget->editItem(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditBookmarksDialog::onRemoveItem() {
|
|
||||||
QList<QTreeWidgetItem*> sels = ui->treeWidget->selectedItems();
|
|
||||||
Q_FOREACH(QTreeWidgetItem* item, sels) {
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
<one line to give the program's name and a brief idea of what it does.>
|
|
||||||
Copyright (C) 2013 PCMan <email>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef FM_EDITBOOKMARKSDIALOG_H
|
|
||||||
#define FM_EDITBOOKMARKSDIALOG_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QDialog>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
|
|
||||||
namespace Ui {
|
|
||||||
class EditBookmarksDialog;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class LIBFM_QT_API EditBookmarksDialog : public QDialog {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit EditBookmarksDialog(FmBookmarks* bookmarks, QWidget* parent = 0, Qt::WindowFlags f = 0);
|
|
||||||
virtual ~EditBookmarksDialog();
|
|
||||||
|
|
||||||
virtual void accept();
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void onAddItem();
|
|
||||||
void onRemoveItem();
|
|
||||||
|
|
||||||
private:
|
|
||||||
Ui::EditBookmarksDialog* ui;
|
|
||||||
FmBookmarks* bookmarks_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_EDITBOOKMARKSDIALOG_H
|
|
@ -1,163 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>ExecFileDialog</class>
|
|
||||||
<widget class="QDialog" name="ExecFileDialog">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>487</width>
|
|
||||||
<height>58</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Execute file</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="0,1">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="icon"/>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="msg">
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="wordWrap">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="open">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Open</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="document-open"/>
|
|
||||||
</property>
|
|
||||||
<property name="default">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="exec">
|
|
||||||
<property name="text">
|
|
||||||
<string>E&xecute</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="system-run"/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="execTerm">
|
|
||||||
<property name="text">
|
|
||||||
<string>Execute in &Terminal</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="utilities-terminal"/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="horizontalSpacer">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="cancel">
|
|
||||||
<property name="text">
|
|
||||||
<string>Cancel</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="dialog-cancel"/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections>
|
|
||||||
<connection>
|
|
||||||
<sender>cancel</sender>
|
|
||||||
<signal>clicked()</signal>
|
|
||||||
<receiver>ExecFileDialog</receiver>
|
|
||||||
<slot>reject()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>341</x>
|
|
||||||
<y>39</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>196</x>
|
|
||||||
<y>28</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
<connection>
|
|
||||||
<sender>exec</sender>
|
|
||||||
<signal>clicked()</signal>
|
|
||||||
<receiver>ExecFileDialog</receiver>
|
|
||||||
<slot>accept()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>56</x>
|
|
||||||
<y>39</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>196</x>
|
|
||||||
<y>28</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
<connection>
|
|
||||||
<sender>execTerm</sender>
|
|
||||||
<signal>clicked()</signal>
|
|
||||||
<receiver>ExecFileDialog</receiver>
|
|
||||||
<slot>accept()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>201</x>
|
|
||||||
<y>39</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>196</x>
|
|
||||||
<y>28</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
<connection>
|
|
||||||
<sender>open</sender>
|
|
||||||
<signal>clicked()</signal>
|
|
||||||
<receiver>ExecFileDialog</receiver>
|
|
||||||
<slot>accept()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>346</x>
|
|
||||||
<y>39</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>250</x>
|
|
||||||
<y>28</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
</connections>
|
|
||||||
</ui>
|
|
@ -1,70 +0,0 @@
|
|||||||
/*
|
|
||||||
* <one line to give the library's name and an idea of what it does.>
|
|
||||||
* Copyright (C) 2014 <copyright holder> <email>
|
|
||||||
*
|
|
||||||
* 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 "execfiledialog_p.h"
|
|
||||||
#include "ui_exec-file.h"
|
|
||||||
#include "icontheme.h"
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
ExecFileDialog::ExecFileDialog(FmFileInfo* file, QWidget* parent, Qt::WindowFlags f):
|
|
||||||
QDialog (parent, f),
|
|
||||||
fileInfo_(fm_file_info_ref(file)),
|
|
||||||
result_(FM_FILE_LAUNCHER_EXEC_CANCEL),
|
|
||||||
ui(new Ui::ExecFileDialog()) {
|
|
||||||
|
|
||||||
ui->setupUi(this);
|
|
||||||
// show file icon
|
|
||||||
FmIcon* icon = fm_file_info_get_icon(fileInfo_);
|
|
||||||
ui->icon->setPixmap(IconTheme::icon(icon).pixmap(QSize(48, 48)));
|
|
||||||
|
|
||||||
QString msg;
|
|
||||||
if(fm_file_info_is_text(file)) {
|
|
||||||
msg = tr("This text file '%1' seems to be an executable script.\nWhat do you want to do with it?")
|
|
||||||
.arg(QString::fromUtf8(fm_file_info_get_disp_name(file)));
|
|
||||||
ui->execTerm->setDefault(true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
msg= tr("This file '%1' is executable. Do you want to execute it?")
|
|
||||||
.arg(QString::fromUtf8(fm_file_info_get_disp_name(file)));
|
|
||||||
ui->exec->setDefault(true);
|
|
||||||
ui->open->hide();
|
|
||||||
}
|
|
||||||
ui->msg->setText(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
ExecFileDialog::~ExecFileDialog() {
|
|
||||||
delete ui;
|
|
||||||
if(fileInfo_)
|
|
||||||
fm_file_info_unref(fileInfo_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ExecFileDialog::accept() {
|
|
||||||
QObject* _sender = sender();
|
|
||||||
if(_sender == ui->exec)
|
|
||||||
result_ = FM_FILE_LAUNCHER_EXEC;
|
|
||||||
else if(_sender == ui->execTerm)
|
|
||||||
result_ = FM_FILE_LAUNCHER_EXEC_IN_TERMINAL;
|
|
||||||
else if(_sender == ui->open)
|
|
||||||
result_ = FM_FILE_LAUNCHER_EXEC_OPEN;
|
|
||||||
QDialog::accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Fm
|
|
@ -1,54 +0,0 @@
|
|||||||
/*
|
|
||||||
* <one line to give the library's name and an idea of what it does.>
|
|
||||||
* Copyright (C) 2014 <copyright holder> <email>
|
|
||||||
*
|
|
||||||
* 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_EXECFILEDIALOG_H
|
|
||||||
#define FM_EXECFILEDIALOG_H
|
|
||||||
|
|
||||||
#include <QDialog>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
|
|
||||||
namespace Ui {
|
|
||||||
class ExecFileDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class ExecFileDialog : public QDialog {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
~ExecFileDialog();
|
|
||||||
ExecFileDialog(FmFileInfo* fileInfo, QWidget* parent = 0, Qt::WindowFlags f = 0);
|
|
||||||
|
|
||||||
FmFileLauncherExecAction result() {
|
|
||||||
return result_;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void accept();
|
|
||||||
|
|
||||||
private:
|
|
||||||
Ui::ExecFileDialog* ui;
|
|
||||||
FmFileInfo* fileInfo_;
|
|
||||||
FmFileLauncherExecAction result_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_EXECFILEDIALOG_H
|
|
@ -1,171 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>FileOperationDialog</class>
|
|
||||||
<widget class="QDialog" name="FileOperationDialog">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>450</width>
|
|
||||||
<height>246</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<layout class="QFormLayout" name="formLayout_2">
|
|
||||||
<item row="0" column="0" colspan="2">
|
|
||||||
<widget class="QLabel" name="message">
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<layout class="QFormLayout" name="formLayout">
|
|
||||||
<property name="fieldGrowthPolicy">
|
|
||||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
|
||||||
</property>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QLabel" name="destLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string>Destination:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QLabel" name="dest">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="wordWrap">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="label_3">
|
|
||||||
<property name="text">
|
|
||||||
<string>Processing:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="QLabel" name="curFile">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Preparing...</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QLabel" name="label_4">
|
|
||||||
<property name="text">
|
|
||||||
<string>Progress</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1">
|
|
||||||
<widget class="QProgressBar" name="progressBar">
|
|
||||||
<property name="alignment">
|
|
||||||
<set>Qt::AlignCenter</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="0">
|
|
||||||
<widget class="QLabel" name="label_5">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Time remaining:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="1">
|
|
||||||
<widget class="QLabel" name="timeRemaining">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0" colspan="2">
|
|
||||||
<widget class="QListWidget" name="sourceFiles">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0" colspan="2">
|
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="standardButtons">
|
|
||||||
<set>QDialogButtonBox::Cancel</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections>
|
|
||||||
<connection>
|
|
||||||
<sender>buttonBox</sender>
|
|
||||||
<signal>accepted()</signal>
|
|
||||||
<receiver>FileOperationDialog</receiver>
|
|
||||||
<slot>accept()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>248</x>
|
|
||||||
<y>254</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>157</x>
|
|
||||||
<y>274</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
<connection>
|
|
||||||
<sender>buttonBox</sender>
|
|
||||||
<signal>rejected()</signal>
|
|
||||||
<receiver>FileOperationDialog</receiver>
|
|
||||||
<slot>reject()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>316</x>
|
|
||||||
<y>260</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>286</x>
|
|
||||||
<y>274</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
</connections>
|
|
||||||
</ui>
|
|
@ -1,736 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>FilePropsDialog</class>
|
|
||||||
<widget class="QDialog" name="FilePropsDialog">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>424</width>
|
|
||||||
<height>456</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>File Properties</string>
|
|
||||||
</property>
|
|
||||||
<property name="windowIcon">
|
|
||||||
<iconset theme="document-properties">
|
|
||||||
<normaloff/>
|
|
||||||
</iconset>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<property name="leftMargin">
|
|
||||||
<number>10</number>
|
|
||||||
</property>
|
|
||||||
<property name="topMargin">
|
|
||||||
<number>10</number>
|
|
||||||
</property>
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>10</number>
|
|
||||||
</property>
|
|
||||||
<property name="bottomMargin">
|
|
||||||
<number>10</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QTabWidget" name="tabWidget">
|
|
||||||
<property name="currentIndex">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="generalPage">
|
|
||||||
<attribute name="title">
|
|
||||||
<string>General</string>
|
|
||||||
</attribute>
|
|
||||||
<layout class="QFormLayout" name="formLayout_2">
|
|
||||||
<property name="horizontalSpacing">
|
|
||||||
<number>12</number>
|
|
||||||
</property>
|
|
||||||
<property name="verticalSpacing">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QPushButton" name="iconButton">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="unknown">
|
|
||||||
<normaloff/>
|
|
||||||
</iconset>
|
|
||||||
</property>
|
|
||||||
<property name="iconSize">
|
|
||||||
<size>
|
|
||||||
<width>32</width>
|
|
||||||
<height>32</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QLineEdit" name="fileName"/>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QLabel" name="label_2">
|
|
||||||
<property name="text">
|
|
||||||
<string>Location:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QLabel" name="location">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="wordWrap">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="textInteractionFlags">
|
|
||||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QLabel" name="label_4">
|
|
||||||
<property name="text">
|
|
||||||
<string>File type:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1">
|
|
||||||
<widget class="QLabel" name="fileType">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="textInteractionFlags">
|
|
||||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="0">
|
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="text">
|
|
||||||
<string>Mime type:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="4" column="1">
|
|
||||||
<widget class="QLabel" name="mimeType">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="textInteractionFlags">
|
|
||||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="6" column="0">
|
|
||||||
<widget class="QLabel" name="label_7">
|
|
||||||
<property name="text">
|
|
||||||
<string>File size:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="6" column="1">
|
|
||||||
<widget class="QLabel" name="fileSize">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="textInteractionFlags">
|
|
||||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="7" column="0">
|
|
||||||
<widget class="QLabel" name="label_6">
|
|
||||||
<property name="text">
|
|
||||||
<string>On-disk size:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="7" column="1">
|
|
||||||
<widget class="QLabel" name="onDiskSize">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="textInteractionFlags">
|
|
||||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="8" column="0">
|
|
||||||
<widget class="QLabel" name="label_3">
|
|
||||||
<property name="text">
|
|
||||||
<string>Last modified:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="8" column="1">
|
|
||||||
<widget class="QLabel" name="lastModified">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="textInteractionFlags">
|
|
||||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="targetLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string>Link target:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="QLabel" name="target">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="wordWrap">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
<property name="textInteractionFlags">
|
|
||||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="0">
|
|
||||||
<widget class="QLabel" name="openWithLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string>Open With:</string>
|
|
||||||
</property>
|
|
||||||
<property name="textInteractionFlags">
|
|
||||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="5" column="1">
|
|
||||||
<widget class="Fm::AppChooserComboBox" name="openWith">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="9" column="0">
|
|
||||||
<widget class="QLabel" name="label_12">
|
|
||||||
<property name="text">
|
|
||||||
<string>Last accessed:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="9" column="1">
|
|
||||||
<widget class="QLabel" name="lastAccessed">
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
<property name="textInteractionFlags">
|
|
||||||
<set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="permissionsPage">
|
|
||||||
<attribute name="title">
|
|
||||||
<string>Permissions</string>
|
|
||||||
</attribute>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
|
||||||
<property name="spacing">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="groupBox_2">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="title">
|
|
||||||
<string>Ownership</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QFormLayout" name="formLayout">
|
|
||||||
<property name="horizontalSpacing">
|
|
||||||
<number>12</number>
|
|
||||||
</property>
|
|
||||||
<property name="verticalSpacing">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QLineEdit" name="owner"/>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="QLineEdit" name="ownerGroup"/>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="label_9">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Group:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QLabel" name="label_8">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Owner:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="groupBox">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="title">
|
|
||||||
<string>Access Control</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
|
||||||
<item>
|
|
||||||
<widget class="QStackedWidget" name="stackedWidget">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="currentIndex">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="page">
|
|
||||||
<layout class="QFormLayout" name="formLayout_3">
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QLabel" name="ownerLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string>Owner:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QComboBox" name="ownerPerm">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QLabel" name="groupLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string>Group:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QComboBox" name="groupPerm">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="otherLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string>Other:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="QComboBox" name="otherPerm">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0" colspan="2">
|
|
||||||
<widget class="QCheckBox" name="executable">
|
|
||||||
<property name="text">
|
|
||||||
<string>Make the file executable</string>
|
|
||||||
</property>
|
|
||||||
<property name="tristate">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="page_2">
|
|
||||||
<layout class="QGridLayout" name="gridLayout_2" columnstretch="1,0,0">
|
|
||||||
<item row="0" column="0" rowspan="3">
|
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
|
||||||
<property name="rightMargin">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<property name="spacing">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QLabel" name="label_5">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Owner:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QCheckBox" name="checkBox">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Read</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="2">
|
|
||||||
<widget class="QCheckBox" name="checkBox_5">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Write</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="3">
|
|
||||||
<widget class="QCheckBox" name="checkBox_8">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Execute</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QLabel" name="label_10">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Group:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QCheckBox" name="checkBox_4">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Read</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="2">
|
|
||||||
<widget class="QCheckBox" name="checkBox_9">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Write</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="3">
|
|
||||||
<widget class="QCheckBox" name="checkBox_3">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Execute</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="label_11">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Other:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="QCheckBox" name="checkBox_7">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Read</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="2">
|
|
||||||
<widget class="QCheckBox" name="checkBox_2">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Write</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="3">
|
|
||||||
<widget class="QCheckBox" name="checkBox_6">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Execute</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="2">
|
|
||||||
<widget class="QCheckBox" name="checkBox_10">
|
|
||||||
<property name="text">
|
|
||||||
<string>Sticky</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="2">
|
|
||||||
<widget class="QCheckBox" name="checkBox_11">
|
|
||||||
<property name="text">
|
|
||||||
<string>SetUID</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="2">
|
|
||||||
<widget class="QCheckBox" name="checkBox_12">
|
|
||||||
<property name="text">
|
|
||||||
<string>SetGID</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1" rowspan="3">
|
|
||||||
<widget class="Line" name="line">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
|
||||||
<item>
|
|
||||||
<spacer name="horizontalSpacer">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>40</width>
|
|
||||||
<height>20</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="pushButton">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Advanced Mode</string>
|
|
||||||
</property>
|
|
||||||
<property name="checkable">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="verticalSpacer">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>40</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="standardButtons">
|
|
||||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<customwidgets>
|
|
||||||
<customwidget>
|
|
||||||
<class>Fm::AppChooserComboBox</class>
|
|
||||||
<extends>QComboBox</extends>
|
|
||||||
<header>appchoosercombobox.h</header>
|
|
||||||
</customwidget>
|
|
||||||
</customwidgets>
|
|
||||||
<resources/>
|
|
||||||
<connections>
|
|
||||||
<connection>
|
|
||||||
<sender>buttonBox</sender>
|
|
||||||
<signal>accepted()</signal>
|
|
||||||
<receiver>FilePropsDialog</receiver>
|
|
||||||
<slot>accept()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>248</x>
|
|
||||||
<y>254</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>157</x>
|
|
||||||
<y>274</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
<connection>
|
|
||||||
<sender>buttonBox</sender>
|
|
||||||
<signal>rejected()</signal>
|
|
||||||
<receiver>FilePropsDialog</receiver>
|
|
||||||
<slot>reject()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>316</x>
|
|
||||||
<y>260</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>286</x>
|
|
||||||
<y>274</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
</connections>
|
|
||||||
</ui>
|
|
@ -1,121 +0,0 @@
|
|||||||
/*
|
|
||||||
<one line to give the library's name and an idea of what it does.>
|
|
||||||
Copyright (C) 2012 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 "filelauncher.h"
|
|
||||||
#include "applaunchcontext.h"
|
|
||||||
#include <QMessageBox>
|
|
||||||
#include <QDebug>
|
|
||||||
#include "execfiledialog_p.h"
|
|
||||||
#include "appchooserdialog.h"
|
|
||||||
#include "utilities.h"
|
|
||||||
|
|
||||||
using namespace Fm;
|
|
||||||
|
|
||||||
FmFileLauncher FileLauncher::funcs = {
|
|
||||||
FileLauncher::_getApp,
|
|
||||||
/* gboolean (*before_open)(GAppLaunchContext* ctx, GList* folder_infos, gpointer user_data); */
|
|
||||||
(FmLaunchFolderFunc)FileLauncher::_openFolder,
|
|
||||||
FileLauncher::_execFile,
|
|
||||||
FileLauncher::_error,
|
|
||||||
FileLauncher::_ask
|
|
||||||
};
|
|
||||||
|
|
||||||
FileLauncher::FileLauncher():
|
|
||||||
quickExec_(false) {
|
|
||||||
}
|
|
||||||
|
|
||||||
FileLauncher::~FileLauncher() {
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
bool FileLauncher::launchFiles(QWidget* parent, GList* file_infos) {
|
|
||||||
FmAppLaunchContext* context = fm_app_launch_context_new_for_widget(parent);
|
|
||||||
bool ret = fm_launch_files(G_APP_LAUNCH_CONTEXT(context), file_infos, &funcs, this);
|
|
||||||
g_object_unref(context);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileLauncher::launchPaths(QWidget* parent, GList* paths) {
|
|
||||||
FmAppLaunchContext* context = fm_app_launch_context_new_for_widget(parent);
|
|
||||||
bool ret = fm_launch_paths(G_APP_LAUNCH_CONTEXT(context), paths, &funcs, this);
|
|
||||||
g_object_unref(context);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
GAppInfo* FileLauncher::getApp(GList* file_infos, FmMimeType* mime_type, GError** err) {
|
|
||||||
AppChooserDialog dlg(NULL);
|
|
||||||
if(mime_type)
|
|
||||||
dlg.setMimeType(mime_type);
|
|
||||||
else
|
|
||||||
dlg.setCanSetDefault(false);
|
|
||||||
// FIXME: show error properly?
|
|
||||||
if(execModelessDialog(&dlg) == QDialog::Accepted) {
|
|
||||||
return dlg.selectedApp();
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileLauncher::openFolder(GAppLaunchContext* ctx, GList* folder_infos, GError** err) {
|
|
||||||
for(GList* l = folder_infos; l; l = l->next) {
|
|
||||||
FmFileInfo* fi = FM_FILE_INFO(l->data);
|
|
||||||
qDebug() << " folder:" << QString::fromUtf8(fm_file_info_get_disp_name(fi));
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
FmFileLauncherExecAction FileLauncher::execFile(FmFileInfo* file) {
|
|
||||||
if (quickExec_) {
|
|
||||||
/* SF bug#838: open terminal for each script may be just a waste.
|
|
||||||
User should open a terminal and start the script there
|
|
||||||
in case if user wants to see the script output anyway.
|
|
||||||
if (fm_file_info_is_text(file))
|
|
||||||
return FM_FILE_LAUNCHER_EXEC_IN_TERMINAL; */
|
|
||||||
return FM_FILE_LAUNCHER_EXEC;
|
|
||||||
}
|
|
||||||
|
|
||||||
FmFileLauncherExecAction res = FM_FILE_LAUNCHER_EXEC_CANCEL;
|
|
||||||
ExecFileDialog dlg(file);
|
|
||||||
if(execModelessDialog(&dlg) == QDialog::Accepted) {
|
|
||||||
res = dlg.result();
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FileLauncher::ask(const char* msg, char* const* btn_labels, int default_btn) {
|
|
||||||
/* FIXME: set default button properly */
|
|
||||||
// return fm_askv(data->parent, NULL, msg, btn_labels);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileLauncher::error(GAppLaunchContext* ctx, GError* err, FmPath* path) {
|
|
||||||
/* ask for mount if trying to launch unmounted path */
|
|
||||||
if(err->domain == G_IO_ERROR) {
|
|
||||||
if(path && err->code == G_IO_ERROR_NOT_MOUNTED) {
|
|
||||||
//if(fm_mount_path(data->parent, path, TRUE))
|
|
||||||
// return FALSE; /* ask to retry */
|
|
||||||
}
|
|
||||||
else if(err->code == G_IO_ERROR_FAILED_HANDLED)
|
|
||||||
return true; /* don't show error message */
|
|
||||||
}
|
|
||||||
QMessageBox dlg(QMessageBox::Critical, QObject::tr("Error"), QString::fromUtf8(err->message), QMessageBox::Ok);
|
|
||||||
execModelessDialog(&dlg);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
@ -1,87 +0,0 @@
|
|||||||
/*
|
|
||||||
<one line to give the library's name and an idea of what it does.>
|
|
||||||
Copyright (C) 2012 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_FILELAUNCHER_H
|
|
||||||
#define FM_FILELAUNCHER_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QWidget>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class LIBFM_QT_API FileLauncher {
|
|
||||||
public:
|
|
||||||
FileLauncher();
|
|
||||||
virtual ~FileLauncher();
|
|
||||||
|
|
||||||
bool launchFiles(QWidget* parent, FmFileInfoList* file_infos) {
|
|
||||||
GList* fileList = fm_file_info_list_peek_head_link(file_infos);
|
|
||||||
return launchFiles(parent, fileList);
|
|
||||||
}
|
|
||||||
bool launchPaths(QWidget* parent, FmPathList* paths) {
|
|
||||||
GList* pathList = fm_path_list_peek_head_link(paths);
|
|
||||||
return launchPaths(parent, pathList);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool launchFiles(QWidget* parent, GList* file_infos);
|
|
||||||
bool launchPaths(QWidget* parent, GList* paths);
|
|
||||||
|
|
||||||
bool quickExec() const {
|
|
||||||
return quickExec_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setQuickExec(bool value) {
|
|
||||||
quickExec_ = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
virtual GAppInfo* getApp(GList* file_infos, FmMimeType* mime_type, GError** err);
|
|
||||||
virtual bool openFolder(GAppLaunchContext* ctx, GList* folder_infos, GError** err);
|
|
||||||
virtual FmFileLauncherExecAction execFile(FmFileInfo* file);
|
|
||||||
virtual bool error(GAppLaunchContext* ctx, GError* err, FmPath* path);
|
|
||||||
virtual int ask(const char* msg, char* const* btn_labels, int default_btn);
|
|
||||||
|
|
||||||
private:
|
|
||||||
static GAppInfo* _getApp(GList* file_infos, FmMimeType* mime_type, gpointer user_data, GError** err) {
|
|
||||||
return reinterpret_cast<FileLauncher*>(user_data)->getApp(file_infos, mime_type, err);
|
|
||||||
}
|
|
||||||
static gboolean _openFolder(GAppLaunchContext* ctx, GList* folder_infos, gpointer user_data, GError** err) {
|
|
||||||
return reinterpret_cast<FileLauncher*>(user_data)->openFolder(ctx, folder_infos, err);
|
|
||||||
}
|
|
||||||
static FmFileLauncherExecAction _execFile(FmFileInfo* file, gpointer user_data) {
|
|
||||||
return reinterpret_cast<FileLauncher*>(user_data)->execFile(file);
|
|
||||||
}
|
|
||||||
static gboolean _error(GAppLaunchContext* ctx, GError* err, FmPath* file, gpointer user_data) {
|
|
||||||
return reinterpret_cast<FileLauncher*>(user_data)->error(ctx, err, file);
|
|
||||||
}
|
|
||||||
static int _ask(const char* msg, char* const* btn_labels, int default_btn, gpointer user_data) {
|
|
||||||
return reinterpret_cast<FileLauncher*>(user_data)->ask(msg, btn_labels, default_btn);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
static FmFileLauncher funcs;
|
|
||||||
bool quickExec_; // Don't ask options on launch executable file
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_FILELAUNCHER_H
|
|
@ -1,396 +0,0 @@
|
|||||||
/*
|
|
||||||
<one line to give the library's name and an idea of what it does.>
|
|
||||||
Copyright (C) 2012 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 "filemenu.h"
|
|
||||||
#include "createnewmenu.h"
|
|
||||||
#include "icontheme.h"
|
|
||||||
#include "filepropsdialog.h"
|
|
||||||
#include "utilities.h"
|
|
||||||
#include "fileoperation.h"
|
|
||||||
#include "filelauncher.h"
|
|
||||||
#include "appchooserdialog.h"
|
|
||||||
#ifdef CUSTOM_ACTIONS
|
|
||||||
#include <libfm/fm-actions.h>
|
|
||||||
#endif
|
|
||||||
#include <QMessageBox>
|
|
||||||
#include <QDebug>
|
|
||||||
#include "filemenu_p.h"
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
FileMenu::FileMenu(FmFileInfoList* files, FmFileInfo* info, FmPath* cwd, QWidget* parent):
|
|
||||||
QMenu(parent),
|
|
||||||
fileLauncher_(NULL) {
|
|
||||||
createMenu(files, info, cwd);
|
|
||||||
}
|
|
||||||
|
|
||||||
FileMenu::FileMenu(FmFileInfoList* files, FmFileInfo* info, FmPath* cwd, const QString& title, QWidget* parent):
|
|
||||||
QMenu(title, parent),
|
|
||||||
fileLauncher_(NULL),
|
|
||||||
unTrashAction_(NULL) {
|
|
||||||
createMenu(files, info, cwd);
|
|
||||||
}
|
|
||||||
|
|
||||||
FileMenu::~FileMenu() {
|
|
||||||
if(files_)
|
|
||||||
fm_file_info_list_unref(files_);
|
|
||||||
if(info_)
|
|
||||||
fm_file_info_unref(info_);
|
|
||||||
if(cwd_)
|
|
||||||
fm_path_unref(cwd_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileMenu::createMenu(FmFileInfoList* files, FmFileInfo* info, FmPath* cwd) {
|
|
||||||
useTrash_ = true;
|
|
||||||
confirmDelete_ = true;
|
|
||||||
confirmTrash_ = false; // Confirm before moving files into "trash can"
|
|
||||||
|
|
||||||
openAction_ = NULL;
|
|
||||||
openWithMenuAction_ = NULL;
|
|
||||||
openWithAction_ = NULL;
|
|
||||||
separator1_ = NULL;
|
|
||||||
cutAction_ = NULL;
|
|
||||||
copyAction_ = NULL;
|
|
||||||
pasteAction_ = NULL;
|
|
||||||
deleteAction_ = NULL;
|
|
||||||
unTrashAction_ = NULL;
|
|
||||||
renameAction_ = NULL;
|
|
||||||
separator2_ = NULL;
|
|
||||||
propertiesAction_ = NULL;
|
|
||||||
|
|
||||||
files_ = fm_file_info_list_ref(files);
|
|
||||||
info_ = info ? fm_file_info_ref(info) : NULL;
|
|
||||||
cwd_ = cwd ? fm_path_ref(cwd) : NULL;
|
|
||||||
|
|
||||||
FmFileInfo* first = fm_file_info_list_peek_head(files);
|
|
||||||
FmMimeType* mime_type = fm_file_info_get_mime_type(first);
|
|
||||||
FmPath* path = fm_file_info_get_path(first);
|
|
||||||
// check if the files are of the same type
|
|
||||||
sameType_ = fm_file_info_list_is_same_type(files);
|
|
||||||
// check if the files are on the same filesystem
|
|
||||||
sameFilesystem_ = fm_file_info_list_is_same_fs(files);
|
|
||||||
// check if the files are all virtual
|
|
||||||
allVirtual_ = sameFilesystem_ && fm_path_is_virtual(path);
|
|
||||||
// check if the files are all in the trash can
|
|
||||||
allTrash_ = sameFilesystem_ && fm_path_is_trash(path);
|
|
||||||
|
|
||||||
openAction_ = new QAction(QIcon::fromTheme("document-open"), tr("Open"), this);
|
|
||||||
connect(openAction_ , &QAction::triggered, this, &FileMenu::onOpenTriggered);
|
|
||||||
addAction(openAction_);
|
|
||||||
|
|
||||||
openWithMenuAction_ = new QAction(tr("Open With..."), this);
|
|
||||||
addAction(openWithMenuAction_);
|
|
||||||
// create the "Open with..." sub menu
|
|
||||||
QMenu* menu = new QMenu();
|
|
||||||
openWithMenuAction_->setMenu(menu);
|
|
||||||
|
|
||||||
if(sameType_) { /* add specific menu items for this mime type */
|
|
||||||
if(mime_type && !allVirtual_) { /* the file has a valid mime-type and its not virtual */
|
|
||||||
GList* apps = g_app_info_get_all_for_type(fm_mime_type_get_type(mime_type));
|
|
||||||
GList* l;
|
|
||||||
for(l=apps;l;l=l->next) {
|
|
||||||
GAppInfo* app = G_APP_INFO(l->data);
|
|
||||||
|
|
||||||
// check if the command really exists
|
|
||||||
gchar * program_path = g_find_program_in_path(g_app_info_get_executable(app));
|
|
||||||
if (!program_path)
|
|
||||||
continue;
|
|
||||||
g_free(program_path);
|
|
||||||
|
|
||||||
// create a QAction for the application.
|
|
||||||
AppInfoAction* action = new AppInfoAction(app);
|
|
||||||
connect(action, &QAction::triggered, this, &FileMenu::onApplicationTriggered);
|
|
||||||
menu->addAction(action);
|
|
||||||
}
|
|
||||||
g_list_free(apps); /* don't unref GAppInfos now */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
menu->addSeparator();
|
|
||||||
openWithAction_ = new QAction(tr("Other Applications"), this);
|
|
||||||
connect(openWithAction_ , &QAction::triggered, this, &FileMenu::onOpenWithTriggered);
|
|
||||||
menu->addAction(openWithAction_);
|
|
||||||
|
|
||||||
separator1_ = addSeparator();
|
|
||||||
|
|
||||||
QAction* createAction = new QAction(tr("Create &New"), this);
|
|
||||||
FmPath* dirPath = fm_file_info_list_get_length(files) == 1 && fm_file_info_is_dir(first)
|
|
||||||
? path : cwd_;
|
|
||||||
createAction->setMenu(new CreateNewMenu(NULL, dirPath, this));
|
|
||||||
addAction(createAction);
|
|
||||||
addSeparator();
|
|
||||||
|
|
||||||
if(allTrash_) { // all selected files are in trash:///
|
|
||||||
bool can_restore = true;
|
|
||||||
/* only immediate children of trash:/// can be restored. */
|
|
||||||
for(GList* l = fm_file_info_list_peek_head_link(files_); l; l=l->next) {
|
|
||||||
FmPath *trash_path = fm_file_info_get_path(FM_FILE_INFO(l->data));
|
|
||||||
if(!fm_path_get_parent(trash_path) ||
|
|
||||||
!fm_path_is_trash_root(fm_path_get_parent(trash_path))) {
|
|
||||||
can_restore = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(can_restore) {
|
|
||||||
unTrashAction_ = new QAction(tr("&Restore"), this);
|
|
||||||
connect(unTrashAction_, &QAction::triggered, this, &FileMenu::onUnTrashTriggered);
|
|
||||||
addAction(unTrashAction_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else { // ordinary files
|
|
||||||
cutAction_ = new QAction(QIcon::fromTheme("edit-cut"), tr("Cut"), this);
|
|
||||||
connect(cutAction_, &QAction::triggered, this, &FileMenu::onCutTriggered);
|
|
||||||
addAction(cutAction_);
|
|
||||||
|
|
||||||
copyAction_ = new QAction(QIcon::fromTheme("edit-copy"), tr("Copy"), this);
|
|
||||||
connect(copyAction_, &QAction::triggered, this, &FileMenu::onCopyTriggered);
|
|
||||||
addAction(copyAction_);
|
|
||||||
|
|
||||||
pasteAction_ = new QAction(QIcon::fromTheme("edit-paste"), tr("Paste"), this);
|
|
||||||
connect(pasteAction_, &QAction::triggered, this, &FileMenu::onPasteTriggered);
|
|
||||||
addAction(pasteAction_);
|
|
||||||
|
|
||||||
deleteAction_ = new QAction(QIcon::fromTheme("user-trash"), tr("&Move to Trash"), this);
|
|
||||||
connect(deleteAction_, &QAction::triggered, this, &FileMenu::onDeleteTriggered);
|
|
||||||
addAction(deleteAction_);
|
|
||||||
|
|
||||||
renameAction_ = new QAction(tr("Rename"), this);
|
|
||||||
connect(renameAction_, &QAction::triggered, this, &FileMenu::onRenameTriggered);
|
|
||||||
addAction(renameAction_);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CUSTOM_ACTIONS
|
|
||||||
// DES-EMA custom actions integration
|
|
||||||
GList* files_list = fm_file_info_list_peek_head_link(files);
|
|
||||||
GList* items = fm_get_actions_for_files(files_list);
|
|
||||||
if(items) {
|
|
||||||
GList* l;
|
|
||||||
for(l=items; l; l=l->next) {
|
|
||||||
FmFileActionItem* item = FM_FILE_ACTION_ITEM(l->data);
|
|
||||||
addCustomActionItem(this, item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g_list_foreach(items, (GFunc)fm_file_action_item_unref, NULL);
|
|
||||||
g_list_free(items);
|
|
||||||
#endif
|
|
||||||
// archiver integration
|
|
||||||
// FIXME: we need to modify upstream libfm to include some Qt-based archiver programs.
|
|
||||||
if(!allVirtual_) {
|
|
||||||
if(sameType_) {
|
|
||||||
FmArchiver* archiver = fm_archiver_get_default();
|
|
||||||
if(archiver) {
|
|
||||||
if(fm_archiver_is_mime_type_supported(archiver, fm_mime_type_get_type(mime_type))) {
|
|
||||||
if(cwd_ && archiver->extract_to_cmd) {
|
|
||||||
QAction* action = new QAction(tr("Extract to..."), this);
|
|
||||||
connect(action, &QAction::triggered, this, &FileMenu::onExtract);
|
|
||||||
addAction(action);
|
|
||||||
}
|
|
||||||
if(archiver->extract_cmd) {
|
|
||||||
QAction* action = new QAction(tr("Extract Here"), this);
|
|
||||||
connect(action, &QAction::triggered, this, &FileMenu::onExtractHere);
|
|
||||||
addAction(action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
QAction* action = new QAction(tr("Compress"), this);
|
|
||||||
connect(action, &QAction::triggered, this, &FileMenu::onCompress);
|
|
||||||
addAction(action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
separator2_ = addSeparator();
|
|
||||||
|
|
||||||
propertiesAction_ = new QAction(QIcon::fromTheme("document-properties"), tr("Properties"), this);
|
|
||||||
connect(propertiesAction_, &QAction::triggered, this, &FileMenu::onFilePropertiesTriggered);
|
|
||||||
addAction(propertiesAction_);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CUSTOM_ACTIONS
|
|
||||||
void FileMenu::addCustomActionItem(QMenu* menu, FmFileActionItem* item) {
|
|
||||||
if(!item) { // separator
|
|
||||||
addSeparator();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this action is not for context menu
|
|
||||||
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, &FileMenu::onCustomActionTrigerred);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void FileMenu::onOpenTriggered() {
|
|
||||||
if(fileLauncher_) {
|
|
||||||
fileLauncher_->launchFiles(NULL, files_);
|
|
||||||
}
|
|
||||||
else { // use the default launcher
|
|
||||||
Fm::FileLauncher launcher;
|
|
||||||
launcher.launchFiles(NULL, files_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileMenu::onOpenWithTriggered() {
|
|
||||||
AppChooserDialog dlg(NULL);
|
|
||||||
if(sameType_) {
|
|
||||||
dlg.setMimeType(fm_file_info_get_mime_type(info_));
|
|
||||||
}
|
|
||||||
else { // we can only set the selected app as default if all files are of the same type
|
|
||||||
dlg.setCanSetDefault(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(execModelessDialog(&dlg) == QDialog::Accepted) {
|
|
||||||
GAppInfo* app = dlg.selectedApp();
|
|
||||||
if(app) {
|
|
||||||
openFilesWithApp(app);
|
|
||||||
g_object_unref(app);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileMenu::openFilesWithApp(GAppInfo* app) {
|
|
||||||
FmPathList* paths = fm_path_list_new_from_file_info_list(files_);
|
|
||||||
GList* uris = NULL;
|
|
||||||
for(GList* l = fm_path_list_peek_head_link(paths); l; l = l->next) {
|
|
||||||
FmPath* path = FM_PATH(l->data);
|
|
||||||
char* uri = fm_path_to_uri(path);
|
|
||||||
uris = g_list_prepend(uris, uri);
|
|
||||||
}
|
|
||||||
fm_path_list_unref(paths);
|
|
||||||
fm_app_info_launch_uris(app, uris, NULL, NULL);
|
|
||||||
g_list_foreach(uris, (GFunc)g_free, NULL);
|
|
||||||
g_list_free(uris);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileMenu::onApplicationTriggered() {
|
|
||||||
AppInfoAction* action = static_cast<AppInfoAction*>(sender());
|
|
||||||
openFilesWithApp(action->appInfo());
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CUSTOM_ACTIONS
|
|
||||||
void FileMenu::onCustomActionTrigerred() {
|
|
||||||
CustomAction* action = static_cast<CustomAction*>(sender());
|
|
||||||
FmFileActionItem* item = action->item();
|
|
||||||
|
|
||||||
GList* files = fm_file_info_list_peek_head_link(files_);
|
|
||||||
char* output = NULL;
|
|
||||||
/* g_debug("item: %s is activated, id:%s", fm_file_action_item_get_name(item),
|
|
||||||
fm_file_action_item_get_id(item)); */
|
|
||||||
fm_file_action_item_launch(item, NULL, files, &output);
|
|
||||||
if(output) {
|
|
||||||
QMessageBox::information(this, tr("Output"), QString::fromUtf8(output));
|
|
||||||
g_free(output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void FileMenu::onFilePropertiesTriggered() {
|
|
||||||
FilePropsDialog::showForFiles(files_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileMenu::onCopyTriggered() {
|
|
||||||
FmPathList* paths = fm_path_list_new_from_file_info_list(files_);
|
|
||||||
Fm::copyFilesToClipboard(paths);
|
|
||||||
fm_path_list_unref(paths);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileMenu::onCutTriggered() {
|
|
||||||
FmPathList* paths = fm_path_list_new_from_file_info_list(files_);
|
|
||||||
Fm::cutFilesToClipboard(paths);
|
|
||||||
fm_path_list_unref(paths);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileMenu::onDeleteTriggered() {
|
|
||||||
FmPathList* paths = fm_path_list_new_from_file_info_list(files_);
|
|
||||||
if(useTrash_)
|
|
||||||
FileOperation::trashFiles(paths, confirmTrash_);
|
|
||||||
else
|
|
||||||
FileOperation::deleteFiles(paths, confirmDelete_);
|
|
||||||
fm_path_list_unref(paths);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileMenu::onUnTrashTriggered() {
|
|
||||||
FmPathList* paths = fm_path_list_new_from_file_info_list(files_);
|
|
||||||
FileOperation::unTrashFiles(paths);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileMenu::onPasteTriggered() {
|
|
||||||
Fm::pasteFilesFromClipboard(cwd_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileMenu::onRenameTriggered() {
|
|
||||||
for(GList* l = fm_file_info_list_peek_head_link(files_); l; l = l->next) {
|
|
||||||
FmFileInfo* info = FM_FILE_INFO(l->data);
|
|
||||||
Fm::renameFile(info, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileMenu::setUseTrash(bool trash) {
|
|
||||||
if(useTrash_ != trash) {
|
|
||||||
useTrash_ = trash;
|
|
||||||
if(deleteAction_) {
|
|
||||||
deleteAction_->setText(useTrash_ ? tr("&Move to Trash") : tr("&Delete"));
|
|
||||||
deleteAction_->setIcon(useTrash_ ? QIcon::fromTheme("user-trash") : QIcon::fromTheme("edit-delete"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileMenu::onCompress() {
|
|
||||||
FmArchiver* archiver = fm_archiver_get_default();
|
|
||||||
if(archiver) {
|
|
||||||
FmPathList* paths = fm_path_list_new_from_file_info_list(files_);
|
|
||||||
fm_archiver_create_archive(archiver, NULL, paths);
|
|
||||||
fm_path_list_unref(paths);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileMenu::onExtract() {
|
|
||||||
FmArchiver* archiver = fm_archiver_get_default();
|
|
||||||
if(archiver) {
|
|
||||||
FmPathList* paths = fm_path_list_new_from_file_info_list(files_);
|
|
||||||
fm_archiver_extract_archives(archiver, NULL, paths);
|
|
||||||
fm_path_list_unref(paths);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileMenu::onExtractHere() {
|
|
||||||
FmArchiver* archiver = fm_archiver_get_default();
|
|
||||||
if(archiver) {
|
|
||||||
FmPathList* paths = fm_path_list_new_from_file_info_list(files_);
|
|
||||||
fm_archiver_extract_archives_to(archiver, NULL, paths, cwd_);
|
|
||||||
fm_path_list_unref(paths);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Fm
|
|
@ -1,208 +0,0 @@
|
|||||||
/*
|
|
||||||
<one line to give the library's name and an idea of what it does.>
|
|
||||||
Copyright (C) 2012 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_FILEMENU_H
|
|
||||||
#define FM_FILEMENU_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QMenu>
|
|
||||||
#include <qabstractitemmodel.h>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
|
|
||||||
class QAction;
|
|
||||||
|
|
||||||
struct _FmFileActionItem;
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class FileLauncher;
|
|
||||||
|
|
||||||
class LIBFM_QT_API FileMenu : public QMenu {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit FileMenu(FmFileInfoList* files, FmFileInfo* info, FmPath* cwd, QWidget* parent = 0);
|
|
||||||
explicit FileMenu(FmFileInfoList* files, FmFileInfo* info, FmPath* cwd, const QString& title, QWidget* parent = 0);
|
|
||||||
~FileMenu();
|
|
||||||
|
|
||||||
bool useTrash() {
|
|
||||||
return useTrash_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setUseTrash(bool trash);
|
|
||||||
|
|
||||||
bool confirmDelete() {
|
|
||||||
return confirmDelete_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setConfirmDelete(bool confirm) {
|
|
||||||
confirmDelete_ = confirm;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction* openAction() {
|
|
||||||
return openAction_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction* openWithMenuAction() {
|
|
||||||
return openWithMenuAction_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction* openWithAction() {
|
|
||||||
return openWithAction_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction* separator1() {
|
|
||||||
return separator1_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction* cutAction() {
|
|
||||||
return cutAction_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction* copyAction() {
|
|
||||||
return copyAction_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction* pasteAction() {
|
|
||||||
return pasteAction_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction* deleteAction() {
|
|
||||||
return deleteAction_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction* unTrashAction() {
|
|
||||||
return unTrashAction_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction* renameAction() {
|
|
||||||
return renameAction_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction* separator2() {
|
|
||||||
return separator2_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction* propertiesAction() {
|
|
||||||
return propertiesAction_;
|
|
||||||
}
|
|
||||||
|
|
||||||
FmFileInfoList* files() {
|
|
||||||
return files_;
|
|
||||||
}
|
|
||||||
|
|
||||||
FmFileInfo* firstFile() {
|
|
||||||
return info_;
|
|
||||||
}
|
|
||||||
|
|
||||||
FmPath* cwd() {
|
|
||||||
return cwd_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setFileLauncher(FileLauncher* launcher) {
|
|
||||||
fileLauncher_ = launcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileLauncher* fileLauncher() {
|
|
||||||
return fileLauncher_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool sameType() const {
|
|
||||||
return sameType_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool sameFilesystem() const {
|
|
||||||
return sameFilesystem_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool allVirtual() const {
|
|
||||||
return allVirtual_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool allTrash() const {
|
|
||||||
return allTrash_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool confirmTrash() const {
|
|
||||||
return confirmTrash_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setConfirmTrash(bool value) {
|
|
||||||
confirmTrash_ = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void createMenu(FmFileInfoList* files, FmFileInfo* info, FmPath* cwd);
|
|
||||||
#ifdef CUSTOM_ACTIONS
|
|
||||||
void addCustomActionItem(QMenu* menu, struct _FmFileActionItem* item);
|
|
||||||
#endif
|
|
||||||
void openFilesWithApp(GAppInfo* app);
|
|
||||||
|
|
||||||
protected Q_SLOTS:
|
|
||||||
void onOpenTriggered();
|
|
||||||
void onOpenWithTriggered();
|
|
||||||
void onFilePropertiesTriggered();
|
|
||||||
void onApplicationTriggered();
|
|
||||||
#ifdef CUSTOM_ACTIONS
|
|
||||||
void onCustomActionTrigerred();
|
|
||||||
#endif
|
|
||||||
void onCompress();
|
|
||||||
void onExtract();
|
|
||||||
void onExtractHere();
|
|
||||||
|
|
||||||
void onCutTriggered();
|
|
||||||
void onCopyTriggered();
|
|
||||||
void onPasteTriggered();
|
|
||||||
void onRenameTriggered();
|
|
||||||
void onDeleteTriggered();
|
|
||||||
void onUnTrashTriggered();
|
|
||||||
|
|
||||||
private:
|
|
||||||
FmFileInfoList* files_;
|
|
||||||
FmFileInfo* info_;
|
|
||||||
FmPath* cwd_;
|
|
||||||
bool useTrash_;
|
|
||||||
bool confirmDelete_;
|
|
||||||
bool confirmTrash_; // Confirm before moving files into "trash can"
|
|
||||||
|
|
||||||
bool sameType_;
|
|
||||||
bool sameFilesystem_;
|
|
||||||
bool allVirtual_;
|
|
||||||
bool allTrash_;
|
|
||||||
|
|
||||||
QAction* openAction_;
|
|
||||||
QAction* openWithMenuAction_;
|
|
||||||
QAction* openWithAction_;
|
|
||||||
QAction* separator1_;
|
|
||||||
QAction* cutAction_;
|
|
||||||
QAction* copyAction_;
|
|
||||||
QAction* pasteAction_;
|
|
||||||
QAction* deleteAction_;
|
|
||||||
QAction* unTrashAction_;
|
|
||||||
QAction* renameAction_;
|
|
||||||
QAction* separator2_;
|
|
||||||
QAction* propertiesAction_;
|
|
||||||
|
|
||||||
FileLauncher* fileLauncher_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_FILEMENU_H
|
|
@ -1,84 +0,0 @@
|
|||||||
/*
|
|
||||||
<one line to give the library's name and an idea of what it does.>
|
|
||||||
Copyright (C) 2012 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_FILEMENU_P_H
|
|
||||||
#define FM_FILEMENU_P_H
|
|
||||||
|
|
||||||
#include "icontheme.h"
|
|
||||||
#ifdef CUSTOM_ACTIONS
|
|
||||||
#include <libfm/fm-actions.h>
|
|
||||||
#endif
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class AppInfoAction : public QAction {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit AppInfoAction(GAppInfo* app, QObject* parent = 0):
|
|
||||||
QAction(QString::fromUtf8(g_app_info_get_name(app)), parent),
|
|
||||||
appInfo_(G_APP_INFO(g_object_ref(app))) {
|
|
||||||
setToolTip(QString::fromUtf8(g_app_info_get_description(app)));
|
|
||||||
GIcon* gicon = g_app_info_get_icon(app);
|
|
||||||
QIcon icon = IconTheme::icon(gicon);
|
|
||||||
setIcon(icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~AppInfoAction() {
|
|
||||||
if(appInfo_)
|
|
||||||
g_object_unref(appInfo_);
|
|
||||||
}
|
|
||||||
|
|
||||||
GAppInfo* appInfo() const {
|
|
||||||
return appInfo_;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
@ -1,304 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "fileoperation.h"
|
|
||||||
#include "fileoperationdialog.h"
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QElapsedTimer>
|
|
||||||
#include <QMessageBox>
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
using namespace Fm;
|
|
||||||
|
|
||||||
#define SHOW_DLG_DELAY 1000
|
|
||||||
|
|
||||||
FileOperation::FileOperation(Type type, FmPathList* srcFiles, QObject* parent):
|
|
||||||
QObject(parent),
|
|
||||||
dlg(NULL),
|
|
||||||
destPath(NULL),
|
|
||||||
srcPaths(fm_path_list_ref(srcFiles)),
|
|
||||||
uiTimer(NULL),
|
|
||||||
elapsedTimer_(NULL),
|
|
||||||
lastElapsed_(0),
|
|
||||||
updateRemainingTime_(true),
|
|
||||||
autoDestroy_(true),
|
|
||||||
job_(fm_file_ops_job_new((FmFileOpType)type, srcFiles)) {
|
|
||||||
|
|
||||||
g_signal_connect(job_, "ask", G_CALLBACK(onFileOpsJobAsk), this);
|
|
||||||
g_signal_connect(job_, "ask-rename", G_CALLBACK(onFileOpsJobAskRename), this);
|
|
||||||
g_signal_connect(job_, "error", G_CALLBACK(onFileOpsJobError), this);
|
|
||||||
g_signal_connect(job_, "prepared", G_CALLBACK(onFileOpsJobPrepared), this);
|
|
||||||
g_signal_connect(job_, "cur-file", G_CALLBACK(onFileOpsJobCurFile), this);
|
|
||||||
g_signal_connect(job_, "percent", G_CALLBACK(onFileOpsJobPercent), this);
|
|
||||||
g_signal_connect(job_, "finished", G_CALLBACK(onFileOpsJobFinished), this);
|
|
||||||
g_signal_connect(job_, "cancelled", G_CALLBACK(onFileOpsJobCancelled), this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileOperation::disconnectJob() {
|
|
||||||
g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobAsk), this);
|
|
||||||
g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobAskRename), this);
|
|
||||||
g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobError), this);
|
|
||||||
g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobPrepared), this);
|
|
||||||
g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobCurFile), this);
|
|
||||||
g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobPercent), this);
|
|
||||||
g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobFinished), this);
|
|
||||||
g_signal_handlers_disconnect_by_func(job_, (gpointer)G_CALLBACK(onFileOpsJobCancelled), this);
|
|
||||||
}
|
|
||||||
|
|
||||||
FileOperation::~FileOperation() {
|
|
||||||
if(uiTimer) {
|
|
||||||
uiTimer->stop();
|
|
||||||
delete uiTimer;
|
|
||||||
uiTimer = NULL;
|
|
||||||
}
|
|
||||||
if(elapsedTimer_) {
|
|
||||||
delete elapsedTimer_;
|
|
||||||
elapsedTimer_ = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(job_) {
|
|
||||||
disconnectJob();
|
|
||||||
g_object_unref(job_);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(srcPaths)
|
|
||||||
fm_path_list_unref(srcPaths);
|
|
||||||
|
|
||||||
if(destPath)
|
|
||||||
fm_path_unref(destPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileOperation::run() {
|
|
||||||
delete uiTimer;
|
|
||||||
// run the job
|
|
||||||
uiTimer = new QTimer();
|
|
||||||
uiTimer->start(SHOW_DLG_DELAY);
|
|
||||||
connect(uiTimer, &QTimer::timeout, this, &FileOperation::onUiTimeout);
|
|
||||||
|
|
||||||
return fm_job_run_async(FM_JOB(job_));
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileOperation::onUiTimeout() {
|
|
||||||
if(dlg) {
|
|
||||||
dlg->setCurFile(curFile);
|
|
||||||
// estimate remaining time based on past history
|
|
||||||
// FIXME: avoid directly access data member of FmFileOpsJob
|
|
||||||
if(Q_LIKELY(job_->percent > 0 && updateRemainingTime_)) {
|
|
||||||
gint64 remaining = elapsedTime() * ((double(100 - job_->percent) / job_->percent) / 1000);
|
|
||||||
dlg->setRemainingTime(remaining);
|
|
||||||
}
|
|
||||||
// this timeout slot is called every 0.5 second.
|
|
||||||
// by adding this flag, we can update remaining time every 1 second.
|
|
||||||
updateRemainingTime_ = !updateRemainingTime_;
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
showDialog();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileOperation::showDialog() {
|
|
||||||
if(!dlg) {
|
|
||||||
dlg = new FileOperationDialog(this);
|
|
||||||
dlg->setSourceFiles(srcPaths);
|
|
||||||
|
|
||||||
if(destPath)
|
|
||||||
dlg->setDestPath(destPath);
|
|
||||||
|
|
||||||
if(curFile.isEmpty()) {
|
|
||||||
dlg->setPrepared();
|
|
||||||
dlg->setCurFile(curFile);
|
|
||||||
}
|
|
||||||
uiTimer->setInterval(500); // change the interval of the timer
|
|
||||||
// now the timer is used to update current file display
|
|
||||||
dlg->show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gint FileOperation::onFileOpsJobAsk(FmFileOpsJob* job, const char* question, char*const* options, FileOperation* pThis) {
|
|
||||||
pThis->pauseElapsedTimer();
|
|
||||||
pThis->showDialog();
|
|
||||||
int ret = pThis->dlg->ask(QString::fromUtf8(question), options);
|
|
||||||
pThis->resumeElapsedTimer();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
gint FileOperation::onFileOpsJobAskRename(FmFileOpsJob* job, FmFileInfo* src, FmFileInfo* dest, char** new_name, FileOperation* pThis) {
|
|
||||||
pThis->pauseElapsedTimer();
|
|
||||||
pThis->showDialog();
|
|
||||||
QString newName;
|
|
||||||
int ret = pThis->dlg->askRename(src, dest, newName);
|
|
||||||
if(!newName.isEmpty()) {
|
|
||||||
*new_name = g_strdup(newName.toUtf8().constData());
|
|
||||||
}
|
|
||||||
pThis->resumeElapsedTimer();
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileOperation::onFileOpsJobCancelled(FmFileOpsJob* job, FileOperation* pThis) {
|
|
||||||
qDebug("file operation is cancelled!");
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileOperation::onFileOpsJobCurFile(FmFileOpsJob* job, const char* cur_file, FileOperation* pThis) {
|
|
||||||
pThis->curFile = QString::fromUtf8(cur_file);
|
|
||||||
|
|
||||||
// We update the current file name in a timeout slot because drawing a string
|
|
||||||
// in the UI is expansive. Updating the label text too often cause
|
|
||||||
// significant impact on performance.
|
|
||||||
// if(pThis->dlg)
|
|
||||||
// pThis->dlg->setCurFile(pThis->curFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
FmJobErrorAction FileOperation::onFileOpsJobError(FmFileOpsJob* job, GError* err, FmJobErrorSeverity severity, FileOperation* pThis) {
|
|
||||||
pThis->pauseElapsedTimer();
|
|
||||||
pThis->showDialog();
|
|
||||||
FmJobErrorAction act = pThis->dlg->error(err, severity);
|
|
||||||
pThis->resumeElapsedTimer();
|
|
||||||
return act;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileOperation::onFileOpsJobFinished(FmFileOpsJob* job, FileOperation* pThis) {
|
|
||||||
pThis->handleFinish();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileOperation::onFileOpsJobPercent(FmFileOpsJob* job, guint percent, FileOperation* pThis) {
|
|
||||||
if(pThis->dlg) {
|
|
||||||
pThis->dlg->setPercent(percent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileOperation::onFileOpsJobPrepared(FmFileOpsJob* job, FileOperation* pThis) {
|
|
||||||
if(!pThis->elapsedTimer_) {
|
|
||||||
pThis->elapsedTimer_ = new QElapsedTimer();
|
|
||||||
pThis->elapsedTimer_->start();
|
|
||||||
}
|
|
||||||
if(pThis->dlg) {
|
|
||||||
pThis->dlg->setPrepared();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileOperation::handleFinish() {
|
|
||||||
disconnectJob();
|
|
||||||
|
|
||||||
if(uiTimer) {
|
|
||||||
uiTimer->stop();
|
|
||||||
delete uiTimer;
|
|
||||||
uiTimer = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(dlg) {
|
|
||||||
dlg->done(QDialog::Accepted);
|
|
||||||
delete dlg;
|
|
||||||
dlg = NULL;
|
|
||||||
}
|
|
||||||
Q_EMIT finished();
|
|
||||||
|
|
||||||
/* sepcial handling for trash
|
|
||||||
* FIXME: need to refactor this to use a more elegant way later. */
|
|
||||||
if(job_->type == FM_FILE_OP_TRASH) { /* FIXME: direct access to job struct! */
|
|
||||||
FmPathList* unable_to_trash = static_cast<FmPathList*>(g_object_get_data(G_OBJECT(job_), "trash-unsupported"));
|
|
||||||
/* some files cannot be trashed because underlying filesystems don't support it. */
|
|
||||||
if(unable_to_trash) { /* delete them instead */
|
|
||||||
/* FIXME: parent window might be already destroyed! */
|
|
||||||
QWidget* parent = NULL; // FIXME: currently, parent window is not set
|
|
||||||
if(QMessageBox::question(parent, tr("Error"),
|
|
||||||
tr("Some files cannot be moved to trash can because "
|
|
||||||
"the underlying file systems don't support this operation.\n"
|
|
||||||
"Do you want to delete them instead?")) == QMessageBox::Yes) {
|
|
||||||
deleteFiles(unable_to_trash, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g_object_unref(job_);
|
|
||||||
job_ = NULL;
|
|
||||||
|
|
||||||
if(autoDestroy_)
|
|
||||||
delete this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
FileOperation* FileOperation::copyFiles(FmPathList* srcFiles, FmPath* dest, QWidget* parent) {
|
|
||||||
FileOperation* op = new FileOperation(FileOperation::Copy, srcFiles);
|
|
||||||
op->setDestination(dest);
|
|
||||||
op->run();
|
|
||||||
return op;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
FileOperation* FileOperation::moveFiles(FmPathList* srcFiles, FmPath* dest, QWidget* parent) {
|
|
||||||
FileOperation* op = new FileOperation(FileOperation::Move, srcFiles);
|
|
||||||
op->setDestination(dest);
|
|
||||||
op->run();
|
|
||||||
return op;
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
FileOperation* FileOperation::symlinkFiles(FmPathList* srcFiles, FmPath* dest, QWidget* parent) {
|
|
||||||
FileOperation* op = new FileOperation(FileOperation::Link, srcFiles);
|
|
||||||
op->setDestination(dest);
|
|
||||||
op->run();
|
|
||||||
return op;
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
FileOperation* FileOperation::deleteFiles(FmPathList* srcFiles, bool prompt, QWidget* parent) {
|
|
||||||
if(prompt) {
|
|
||||||
int result = QMessageBox::warning(parent, tr("Confirm"),
|
|
||||||
tr("Do you want to delete the selected files?"),
|
|
||||||
QMessageBox::Yes|QMessageBox::No,
|
|
||||||
QMessageBox::No);
|
|
||||||
if(result != QMessageBox::Yes)
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileOperation* op = new FileOperation(FileOperation::Delete, srcFiles);
|
|
||||||
op->run();
|
|
||||||
return op;
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
FileOperation* FileOperation::trashFiles(FmPathList* srcFiles, bool prompt, QWidget* parent) {
|
|
||||||
if(prompt) {
|
|
||||||
int result = QMessageBox::warning(parent, tr("Confirm"),
|
|
||||||
tr("Do you want to move the selected files to trash can?"),
|
|
||||||
QMessageBox::Yes|QMessageBox::No,
|
|
||||||
QMessageBox::No);
|
|
||||||
if(result != QMessageBox::Yes)
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileOperation* op = new FileOperation(FileOperation::Trash, srcFiles);
|
|
||||||
op->run();
|
|
||||||
return op;
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
FileOperation* FileOperation::unTrashFiles(FmPathList* srcFiles, QWidget* parent) {
|
|
||||||
FileOperation* op = new FileOperation(FileOperation::UnTrash, srcFiles);
|
|
||||||
op->run();
|
|
||||||
return op;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
FileOperation* FileOperation::changeAttrFiles(FmPathList* srcFiles, QWidget* parent) {
|
|
||||||
//TODO
|
|
||||||
FileOperation* op = new FileOperation(FileOperation::ChangeAttr, srcFiles);
|
|
||||||
op->run();
|
|
||||||
return op;
|
|
||||||
}
|
|
@ -1,164 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef FM_FILEOPERATION_H
|
|
||||||
#define FM_FILEOPERATION_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QObject>
|
|
||||||
#include <QElapsedTimer>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
|
|
||||||
class QTimer;
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class FileOperationDialog;
|
|
||||||
|
|
||||||
class LIBFM_QT_API FileOperation : public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
enum Type {
|
|
||||||
Copy = FM_FILE_OP_COPY,
|
|
||||||
Move = FM_FILE_OP_MOVE,
|
|
||||||
Link = FM_FILE_OP_LINK,
|
|
||||||
Delete = FM_FILE_OP_DELETE,
|
|
||||||
Trash = FM_FILE_OP_TRASH,
|
|
||||||
UnTrash = FM_FILE_OP_UNTRASH,
|
|
||||||
ChangeAttr = FM_FILE_OP_CHANGE_ATTR
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit FileOperation(Type type, FmPathList* srcFiles, QObject* parent = 0);
|
|
||||||
virtual ~FileOperation();
|
|
||||||
|
|
||||||
void setDestination(FmPath* dest) {
|
|
||||||
destPath = fm_path_ref(dest);
|
|
||||||
fm_file_ops_job_set_dest(job_, dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setChmod(mode_t newMode, mode_t newModeMask) {
|
|
||||||
fm_file_ops_job_set_chmod(job_, newMode, newModeMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setChown(gint uid, gint gid) {
|
|
||||||
fm_file_ops_job_set_chown(job_, uid, gid);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This only work for change attr jobs.
|
|
||||||
void setRecursiveChattr(bool recursive) {
|
|
||||||
fm_file_ops_job_set_recursive(job_, (gboolean)recursive);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool run();
|
|
||||||
|
|
||||||
void cancel() {
|
|
||||||
if(job_)
|
|
||||||
fm_job_cancel(FM_JOB(job_));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isRunning() const {
|
|
||||||
return job_ ? fm_job_is_running(FM_JOB(job_)) : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isCancelled() const {
|
|
||||||
return job_ ? fm_job_is_cancelled(FM_JOB(job_)) : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
FmFileOpsJob* job() {
|
|
||||||
return job_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool autoDestroy() {
|
|
||||||
return autoDestroy_;
|
|
||||||
}
|
|
||||||
void setAutoDestroy(bool destroy = true) {
|
|
||||||
autoDestroy_ = destroy;
|
|
||||||
}
|
|
||||||
|
|
||||||
Type type() {
|
|
||||||
return (Type)job_->type;
|
|
||||||
}
|
|
||||||
|
|
||||||
// convinient static functions
|
|
||||||
static FileOperation* copyFiles(FmPathList* srcFiles, FmPath* dest, QWidget* parent = 0);
|
|
||||||
static FileOperation* moveFiles(FmPathList* srcFiles, FmPath* dest, QWidget* parent = 0);
|
|
||||||
static FileOperation* symlinkFiles(FmPathList* srcFiles, FmPath* dest, QWidget* parent = 0);
|
|
||||||
static FileOperation* deleteFiles(FmPathList* srcFiles, bool promp = true, QWidget* parent = 0);
|
|
||||||
static FileOperation* trashFiles(FmPathList* srcFiles, bool promp = true, QWidget* parent = 0);
|
|
||||||
static FileOperation* unTrashFiles(FmPathList* srcFiles, QWidget* parent = 0);
|
|
||||||
static FileOperation* changeAttrFiles(FmPathList* srcFiles, QWidget* parent = 0);
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void finished();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static gint onFileOpsJobAsk(FmFileOpsJob* job, const char* question, char* const* options, FileOperation* pThis);
|
|
||||||
static gint onFileOpsJobAskRename(FmFileOpsJob* job, FmFileInfo* src, FmFileInfo* dest, char** new_name, FileOperation* pThis);
|
|
||||||
static FmJobErrorAction onFileOpsJobError(FmFileOpsJob* job, GError* err, FmJobErrorSeverity severity, FileOperation* pThis);
|
|
||||||
static void onFileOpsJobPrepared(FmFileOpsJob* job, FileOperation* pThis);
|
|
||||||
static void onFileOpsJobCurFile(FmFileOpsJob* job, const char* cur_file, FileOperation* pThis);
|
|
||||||
static void onFileOpsJobPercent(FmFileOpsJob* job, guint percent, FileOperation* pThis);
|
|
||||||
static void onFileOpsJobFinished(FmFileOpsJob* job, FileOperation* pThis);
|
|
||||||
static void onFileOpsJobCancelled(FmFileOpsJob* job, FileOperation* pThis);
|
|
||||||
|
|
||||||
void handleFinish();
|
|
||||||
void disconnectJob();
|
|
||||||
void showDialog();
|
|
||||||
|
|
||||||
void pauseElapsedTimer() {
|
|
||||||
if(Q_LIKELY(elapsedTimer_ != NULL)) {
|
|
||||||
lastElapsed_ += elapsedTimer_->elapsed();
|
|
||||||
elapsedTimer_->invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void resumeElapsedTimer() {
|
|
||||||
if(Q_LIKELY(elapsedTimer_ != NULL)) {
|
|
||||||
elapsedTimer_->start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
qint64 elapsedTime() {
|
|
||||||
if(Q_LIKELY(elapsedTimer_ != NULL)) {
|
|
||||||
return lastElapsed_ + elapsedTimer_->elapsed();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void onUiTimeout();
|
|
||||||
|
|
||||||
private:
|
|
||||||
FmFileOpsJob* job_;
|
|
||||||
FileOperationDialog* dlg;
|
|
||||||
FmPath* destPath;
|
|
||||||
FmPathList* srcPaths;
|
|
||||||
QTimer* uiTimer;
|
|
||||||
QElapsedTimer* elapsedTimer_;
|
|
||||||
qint64 lastElapsed_;
|
|
||||||
bool updateRemainingTime_;
|
|
||||||
QString curFile;
|
|
||||||
bool autoDestroy_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_FILEOPERATION_H
|
|
@ -1,176 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "fileoperationdialog.h"
|
|
||||||
#include "fileoperation.h"
|
|
||||||
#include "renamedialog.h"
|
|
||||||
#include <QMessageBox>
|
|
||||||
#include "ui_file-operation-dialog.h"
|
|
||||||
|
|
||||||
using namespace Fm;
|
|
||||||
|
|
||||||
FileOperationDialog::FileOperationDialog(FileOperation* _operation):
|
|
||||||
QDialog(NULL),
|
|
||||||
operation(_operation),
|
|
||||||
defaultOption(-1) {
|
|
||||||
|
|
||||||
ui = new Ui::FileOperationDialog();
|
|
||||||
ui->setupUi(this);
|
|
||||||
|
|
||||||
QString title;
|
|
||||||
QString message;
|
|
||||||
switch(_operation->type()) {
|
|
||||||
case FM_FILE_OP_MOVE:
|
|
||||||
title = tr("Move files");
|
|
||||||
message = tr("Moving the following files to destination folder:");
|
|
||||||
break;
|
|
||||||
case FM_FILE_OP_COPY:
|
|
||||||
title = tr("Copy Files");
|
|
||||||
message = tr("Copying the following files to destination folder:");
|
|
||||||
break;
|
|
||||||
case FM_FILE_OP_TRASH:
|
|
||||||
title = tr("Trash Files");
|
|
||||||
message = tr("Moving the following files to trash can:");
|
|
||||||
break;
|
|
||||||
case FM_FILE_OP_DELETE:
|
|
||||||
title = tr("Delete Files");
|
|
||||||
message = tr("Deleting the following files");
|
|
||||||
ui->dest->hide();
|
|
||||||
ui->destLabel->hide();
|
|
||||||
break;
|
|
||||||
case FM_FILE_OP_LINK:
|
|
||||||
title = tr("Create Symlinks");
|
|
||||||
message = tr("Creating symlinks for the following files:");
|
|
||||||
break;
|
|
||||||
case FM_FILE_OP_CHANGE_ATTR:
|
|
||||||
title = tr("Change Attributes");
|
|
||||||
message = tr("Changing attributes of the following files:");
|
|
||||||
ui->dest->hide();
|
|
||||||
ui->destLabel->hide();
|
|
||||||
break;
|
|
||||||
case FM_FILE_OP_UNTRASH:
|
|
||||||
title = tr("Restore Trashed Files");
|
|
||||||
message = tr("Restoring the following files from trash can:");
|
|
||||||
ui->dest->hide();
|
|
||||||
ui->destLabel->hide();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ui->message->setText(message);
|
|
||||||
setWindowTitle(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
FileOperationDialog::~FileOperationDialog() {
|
|
||||||
delete ui;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileOperationDialog::setDestPath(FmPath* dest) {
|
|
||||||
char* pathStr = fm_path_display_name(dest, false);
|
|
||||||
ui->dest->setText(QString::fromUtf8(pathStr));
|
|
||||||
g_free(pathStr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileOperationDialog::setSourceFiles(FmPathList* srcFiles) {
|
|
||||||
GList* l;
|
|
||||||
for(l = fm_path_list_peek_head_link(srcFiles); l; l = l->next) {
|
|
||||||
FmPath* path = FM_PATH(l->data);
|
|
||||||
char* pathStr = fm_path_display_name(path, false);
|
|
||||||
ui->sourceFiles->addItem(QString::fromUtf8(pathStr));
|
|
||||||
g_free(pathStr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int FileOperationDialog::ask(QString question, char*const* options) {
|
|
||||||
// TODO: implement FileOperationDialog::ask()
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int FileOperationDialog::askRename(FmFileInfo* src, FmFileInfo* dest, QString& new_name) {
|
|
||||||
int ret;
|
|
||||||
if(defaultOption == -1) { // default action is not set, ask the user
|
|
||||||
RenameDialog dlg(src, dest, this);
|
|
||||||
dlg.exec();
|
|
||||||
switch(dlg.action()) {
|
|
||||||
case RenameDialog::ActionOverwrite:
|
|
||||||
ret = FM_FILE_OP_OVERWRITE;
|
|
||||||
if(dlg.applyToAll())
|
|
||||||
defaultOption = ret;
|
|
||||||
break;
|
|
||||||
case RenameDialog::ActionRename:
|
|
||||||
ret = FM_FILE_OP_RENAME;
|
|
||||||
new_name = dlg.newName();
|
|
||||||
break;
|
|
||||||
case RenameDialog::ActionIgnore:
|
|
||||||
ret = FM_FILE_OP_SKIP;
|
|
||||||
if(dlg.applyToAll())
|
|
||||||
defaultOption = ret;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ret = FM_FILE_OP_CANCEL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ret = defaultOption;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
FmJobErrorAction FileOperationDialog::error(GError* err, FmJobErrorSeverity severity) {
|
|
||||||
if(severity >= FM_JOB_ERROR_MODERATE) {
|
|
||||||
QMessageBox::critical(this, tr("Error"), QString::fromUtf8(err->message));
|
|
||||||
if(severity == FM_JOB_ERROR_CRITICAL)
|
|
||||||
return FM_JOB_ABORT;
|
|
||||||
}
|
|
||||||
return FM_JOB_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileOperationDialog::setCurFile(QString cur_file) {
|
|
||||||
ui->curFile->setText(cur_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileOperationDialog::setPercent(unsigned int percent) {
|
|
||||||
ui->progressBar->setValue(percent);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileOperationDialog::setRemainingTime(unsigned int sec) {
|
|
||||||
unsigned int min = 0;
|
|
||||||
unsigned int hr = 0;
|
|
||||||
if(sec > 60) {
|
|
||||||
min = sec / 60;
|
|
||||||
sec %= 60;
|
|
||||||
if(min > 60) {
|
|
||||||
hr = min / 60;
|
|
||||||
min %= 60;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ui->timeRemaining->setText(QString("%1:%2:%3")
|
|
||||||
.arg(hr, 2, 10, QChar('0'))
|
|
||||||
.arg(min, 2, 10, QChar('0'))
|
|
||||||
.arg(sec, 2, 10, QChar('0')));
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileOperationDialog::setPrepared() {
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileOperationDialog::reject() {
|
|
||||||
operation->cancel();
|
|
||||||
QDialog::reject();
|
|
||||||
}
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef FM_FILEOPERATIONDIALOG_H
|
|
||||||
#define FM_FILEOPERATIONDIALOG_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QDialog>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
|
|
||||||
namespace Ui {
|
|
||||||
class FileOperationDialog;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class FileOperation;
|
|
||||||
|
|
||||||
class LIBFM_QT_API FileOperationDialog : public QDialog {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit FileOperationDialog(FileOperation* _operation);
|
|
||||||
virtual ~FileOperationDialog();
|
|
||||||
|
|
||||||
void setSourceFiles(FmPathList* srcFiles);
|
|
||||||
void setDestPath(FmPath* dest);
|
|
||||||
|
|
||||||
int ask(QString question, char* const* options);
|
|
||||||
int askRename(FmFileInfo* src, FmFileInfo* dest, QString& new_name);
|
|
||||||
FmJobErrorAction error(GError* err, FmJobErrorSeverity severity);
|
|
||||||
void setPrepared();
|
|
||||||
void setCurFile(QString cur_file);
|
|
||||||
void setPercent(unsigned int percent);
|
|
||||||
void setRemainingTime(unsigned int sec);
|
|
||||||
|
|
||||||
virtual void reject();
|
|
||||||
|
|
||||||
private:
|
|
||||||
Ui::FileOperationDialog* ui;
|
|
||||||
FileOperation* operation;
|
|
||||||
int defaultOption;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_FILEOPERATIONDIALOG_H
|
|
@ -1,441 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "filepropsdialog.h"
|
|
||||||
#include "ui_file-props.h"
|
|
||||||
#include "icontheme.h"
|
|
||||||
#include "utilities.h"
|
|
||||||
#include "fileoperation.h"
|
|
||||||
#include <QStringBuilder>
|
|
||||||
#include <QStringListModel>
|
|
||||||
#include <QMessageBox>
|
|
||||||
#include <qdial.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <time.h>
|
|
||||||
|
|
||||||
#define DIFFERENT_UIDS ((uid)-1)
|
|
||||||
#define DIFFERENT_GIDS ((gid)-1)
|
|
||||||
#define DIFFERENT_PERMS ((mode_t)-1)
|
|
||||||
|
|
||||||
using namespace Fm;
|
|
||||||
|
|
||||||
enum {
|
|
||||||
ACCESS_NO_CHANGE = 0,
|
|
||||||
ACCESS_READ_ONLY,
|
|
||||||
ACCESS_READ_WRITE,
|
|
||||||
ACCESS_FORBID
|
|
||||||
};
|
|
||||||
|
|
||||||
FilePropsDialog::FilePropsDialog(FmFileInfoList* files, QWidget* parent, Qt::WindowFlags f):
|
|
||||||
QDialog(parent, f),
|
|
||||||
fileInfos_(fm_file_info_list_ref(files)),
|
|
||||||
singleType(fm_file_info_list_is_same_type(files)),
|
|
||||||
singleFile(fm_file_info_list_get_length(files) == 1 ? true:false),
|
|
||||||
fileInfo(fm_file_info_list_peek_head(files)),
|
|
||||||
mimeType(NULL) {
|
|
||||||
|
|
||||||
setAttribute(Qt::WA_DeleteOnClose);
|
|
||||||
|
|
||||||
ui = new Ui::FilePropsDialog();
|
|
||||||
ui->setupUi(this);
|
|
||||||
|
|
||||||
if(singleType) {
|
|
||||||
mimeType = fm_mime_type_ref(fm_file_info_get_mime_type(fileInfo));
|
|
||||||
}
|
|
||||||
|
|
||||||
FmPathList* paths = fm_path_list_new_from_file_info_list(files);
|
|
||||||
deepCountJob = fm_deep_count_job_new(paths, FM_DC_JOB_DEFAULT);
|
|
||||||
fm_path_list_unref(paths);
|
|
||||||
|
|
||||||
initGeneralPage();
|
|
||||||
initPermissionsPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
FilePropsDialog::~FilePropsDialog() {
|
|
||||||
delete ui;
|
|
||||||
|
|
||||||
if(fileInfos_)
|
|
||||||
fm_file_info_list_unref(fileInfos_);
|
|
||||||
if(deepCountJob)
|
|
||||||
g_object_unref(deepCountJob);
|
|
||||||
if(fileSizeTimer) {
|
|
||||||
fileSizeTimer->stop();
|
|
||||||
delete fileSizeTimer;
|
|
||||||
fileSizeTimer = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FilePropsDialog::initApplications() {
|
|
||||||
if(singleType && mimeType && !fm_file_info_is_dir(fileInfo)) {
|
|
||||||
ui->openWith->setMimeType(mimeType);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ui->openWith->hide();
|
|
||||||
ui->openWithLabel->hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FilePropsDialog::initPermissionsPage() {
|
|
||||||
// ownership handling
|
|
||||||
// get owner/group and mode of the first file in the list
|
|
||||||
uid = fm_file_info_get_uid(fileInfo);
|
|
||||||
gid = fm_file_info_get_gid(fileInfo);
|
|
||||||
mode_t mode = fm_file_info_get_mode(fileInfo);
|
|
||||||
ownerPerm = (mode & (S_IRUSR|S_IWUSR|S_IXUSR));
|
|
||||||
groupPerm = (mode & (S_IRGRP|S_IWGRP|S_IXGRP));
|
|
||||||
otherPerm = (mode & (S_IROTH|S_IWOTH|S_IXOTH));
|
|
||||||
execPerm = (mode & (S_IXUSR|S_IXGRP|S_IXOTH));
|
|
||||||
allNative = fm_file_info_is_native(fileInfo);
|
|
||||||
hasDir = S_ISDIR(mode);
|
|
||||||
|
|
||||||
// check if all selected files belongs to the same owner/group or have the same mode
|
|
||||||
// at the same time, check if all files are on native unix filesystems
|
|
||||||
GList* l;
|
|
||||||
for(l = fm_file_info_list_peek_head_link(fileInfos_)->next; l; l = l->next) {
|
|
||||||
FmFileInfo* fi = FM_FILE_INFO(l->data);
|
|
||||||
if(allNative && !fm_file_info_is_native(fi))
|
|
||||||
allNative = false; // not all of the files are native
|
|
||||||
|
|
||||||
mode_t fi_mode = fm_file_info_get_mode(fi);
|
|
||||||
if(S_ISDIR(fi_mode))
|
|
||||||
hasDir = true; // the files list contains dir(s)
|
|
||||||
|
|
||||||
if(uid != DIFFERENT_UIDS && uid != fm_file_info_get_uid(fi))
|
|
||||||
uid = DIFFERENT_UIDS; // not all files have the same owner
|
|
||||||
if(gid != DIFFERENT_GIDS && gid != fm_file_info_get_gid(fi))
|
|
||||||
gid = DIFFERENT_GIDS; // not all files have the same owner group
|
|
||||||
|
|
||||||
if(ownerPerm != DIFFERENT_PERMS && ownerPerm != (fi_mode & (S_IRUSR|S_IWUSR|S_IXUSR)))
|
|
||||||
ownerPerm = DIFFERENT_PERMS; // not all files have the same permission for owner
|
|
||||||
if(groupPerm != DIFFERENT_PERMS && groupPerm != (fi_mode & (S_IRGRP|S_IWGRP|S_IXGRP)))
|
|
||||||
groupPerm = DIFFERENT_PERMS; // not all files have the same permission for grop
|
|
||||||
if(otherPerm != DIFFERENT_PERMS && otherPerm != (fi_mode & (S_IROTH|S_IWOTH|S_IXOTH)))
|
|
||||||
otherPerm = DIFFERENT_PERMS; // not all files have the same permission for other
|
|
||||||
if(execPerm != DIFFERENT_PERMS && execPerm != (fi_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
|
|
||||||
execPerm = DIFFERENT_PERMS; // not all files have the same executable permission
|
|
||||||
}
|
|
||||||
|
|
||||||
// init owner/group
|
|
||||||
initOwner();
|
|
||||||
|
|
||||||
// if all files are of the same type, and some of them are dirs => all of the items are dirs
|
|
||||||
// rwx values have different meanings for dirs
|
|
||||||
// Let's make it clear for the users
|
|
||||||
// init combo boxes for file permissions here
|
|
||||||
QStringList comboItems;
|
|
||||||
comboItems.append("---"); // no change
|
|
||||||
if(singleType && hasDir) { // all files are dirs
|
|
||||||
comboItems.append(tr("View folder content"));
|
|
||||||
comboItems.append(tr("View and modify folder content"));
|
|
||||||
ui->executable->hide();
|
|
||||||
}
|
|
||||||
else { //not all of the files are dirs
|
|
||||||
comboItems.append(tr("Read"));
|
|
||||||
comboItems.append(tr("Read and write"));
|
|
||||||
}
|
|
||||||
comboItems.append(tr("Forbidden"));
|
|
||||||
QStringListModel* comboModel = new QStringListModel(comboItems, this);
|
|
||||||
ui->ownerPerm->setModel(comboModel);
|
|
||||||
ui->groupPerm->setModel(comboModel);
|
|
||||||
ui->otherPerm->setModel(comboModel);
|
|
||||||
|
|
||||||
// owner
|
|
||||||
ownerPermSel = ACCESS_NO_CHANGE;
|
|
||||||
if(ownerPerm != DIFFERENT_PERMS) { // permissions for owner are the same among all files
|
|
||||||
if(ownerPerm & S_IRUSR) { // can read
|
|
||||||
if(ownerPerm & S_IWUSR) // can write
|
|
||||||
ownerPermSel = ACCESS_READ_WRITE;
|
|
||||||
else
|
|
||||||
ownerPermSel = ACCESS_READ_ONLY;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if((ownerPerm & S_IWUSR) == 0) // cannot read or write
|
|
||||||
ownerPermSel = ACCESS_FORBID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ui->ownerPerm->setCurrentIndex(ownerPermSel);
|
|
||||||
|
|
||||||
// owner and group
|
|
||||||
groupPermSel = ACCESS_NO_CHANGE;
|
|
||||||
if(groupPerm != DIFFERENT_PERMS) { // permissions for owner are the same among all files
|
|
||||||
if(groupPerm & S_IRGRP) { // can read
|
|
||||||
if(groupPerm & S_IWGRP) // can write
|
|
||||||
groupPermSel = ACCESS_READ_WRITE;
|
|
||||||
else
|
|
||||||
groupPermSel = ACCESS_READ_ONLY;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if((groupPerm & S_IWGRP) == 0) // cannot read or write
|
|
||||||
groupPermSel = ACCESS_FORBID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ui->groupPerm->setCurrentIndex(groupPermSel);
|
|
||||||
|
|
||||||
// other
|
|
||||||
otherPermSel = ACCESS_NO_CHANGE;
|
|
||||||
if(otherPerm != DIFFERENT_PERMS) { // permissions for owner are the same among all files
|
|
||||||
if(otherPerm & S_IROTH) { // can read
|
|
||||||
if(otherPerm & S_IWOTH) // can write
|
|
||||||
otherPermSel = ACCESS_READ_WRITE;
|
|
||||||
else
|
|
||||||
otherPermSel = ACCESS_READ_ONLY;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if((otherPerm & S_IWOTH) == 0) // cannot read or write
|
|
||||||
otherPermSel = ACCESS_FORBID;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
ui->otherPerm->setCurrentIndex(otherPermSel);
|
|
||||||
|
|
||||||
// set the checkbox to partially checked state
|
|
||||||
// when owner, group, and other have different executable flags set.
|
|
||||||
// some of them have exec, and others do not have.
|
|
||||||
execCheckState = Qt::PartiallyChecked;
|
|
||||||
if(execPerm != DIFFERENT_PERMS) { // if all files have the same executable permission
|
|
||||||
// check if the files are all executable
|
|
||||||
if((mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == (S_IXUSR|S_IXGRP|S_IXOTH)) {
|
|
||||||
// owner, group, and other all have exec permission.
|
|
||||||
ui->executable->setTristate(false);
|
|
||||||
execCheckState = Qt::Checked;
|
|
||||||
}
|
|
||||||
else if((mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0) {
|
|
||||||
// owner, group, and other all have no exec permission
|
|
||||||
ui->executable->setTristate(false);
|
|
||||||
execCheckState = Qt::Unchecked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ui->executable->setCheckState(execCheckState);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FilePropsDialog::initGeneralPage() {
|
|
||||||
// update UI
|
|
||||||
if(singleType) { // all files are of the same mime-type
|
|
||||||
FmIcon* icon = NULL;
|
|
||||||
// FIXME: handle custom icons for some files
|
|
||||||
// FIXME: display special property pages for special files or
|
|
||||||
// some specified mime-types.
|
|
||||||
if(singleFile) { // only one file is selected.
|
|
||||||
icon = fm_file_info_get_icon(fileInfo);
|
|
||||||
}
|
|
||||||
if(mimeType) {
|
|
||||||
if(!icon) // get an icon from mime type if needed
|
|
||||||
icon = fm_mime_type_get_icon(mimeType);
|
|
||||||
ui->fileType->setText(QString::fromUtf8(fm_mime_type_get_desc(mimeType)));
|
|
||||||
ui->mimeType->setText(QString::fromUtf8(fm_mime_type_get_type(mimeType)));
|
|
||||||
}
|
|
||||||
if(icon) {
|
|
||||||
ui->iconButton->setIcon(IconTheme::icon(icon));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(singleFile && fm_file_info_is_symlink(fileInfo)) {
|
|
||||||
ui->target->setText(QString::fromUtf8(fm_file_info_get_target(fileInfo)));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ui->target->hide();
|
|
||||||
ui->targetLabel->hide();
|
|
||||||
}
|
|
||||||
} // end if(singleType)
|
|
||||||
else { // not singleType, multiple files are selected at the same time
|
|
||||||
ui->fileType->setText(tr("Files of different types"));
|
|
||||||
ui->target->hide();
|
|
||||||
ui->targetLabel->hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: check if all files has the same parent dir, mtime, or atime
|
|
||||||
if(singleFile) { // only one file is selected
|
|
||||||
FmPath* parent_path = fm_path_get_parent(fm_file_info_get_path(fileInfo));
|
|
||||||
char* parent_str = parent_path ? fm_path_display_name(parent_path, true) : NULL;
|
|
||||||
|
|
||||||
ui->fileName->setText(QString::fromUtf8(fm_file_info_get_disp_name(fileInfo)));
|
|
||||||
if(parent_str) {
|
|
||||||
ui->location->setText(QString::fromUtf8(parent_str));
|
|
||||||
g_free(parent_str);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ui->location->clear();
|
|
||||||
|
|
||||||
ui->lastModified->setText(QString::fromUtf8(fm_file_info_get_disp_mtime(fileInfo)));
|
|
||||||
|
|
||||||
// FIXME: need to encapsulate this in an libfm API.
|
|
||||||
time_t atime;
|
|
||||||
struct tm tm;
|
|
||||||
atime = fm_file_info_get_atime(fileInfo);
|
|
||||||
localtime_r(&atime, &tm);
|
|
||||||
char buf[128];
|
|
||||||
strftime(buf, sizeof(buf), "%x %R", &tm);
|
|
||||||
ui->lastAccessed->setText(QString::fromUtf8(buf));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ui->fileName->setText(tr("Multiple Files"));
|
|
||||||
ui->fileName->setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
initApplications(); // init applications combo box
|
|
||||||
|
|
||||||
// calculate total file sizes
|
|
||||||
fileSizeTimer = new QTimer(this);
|
|
||||||
connect(fileSizeTimer, &QTimer::timeout, this, &FilePropsDialog::onFileSizeTimerTimeout);
|
|
||||||
fileSizeTimer->start(600);
|
|
||||||
g_signal_connect(deepCountJob, "finished", G_CALLBACK(onDeepCountJobFinished), this);
|
|
||||||
fm_job_run_async(FM_JOB(deepCountJob));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*static */ void FilePropsDialog::onDeepCountJobFinished(FmDeepCountJob* job, FilePropsDialog* pThis) {
|
|
||||||
|
|
||||||
pThis->onFileSizeTimerTimeout(); // update file size display
|
|
||||||
|
|
||||||
// free the job
|
|
||||||
g_object_unref(pThis->deepCountJob);
|
|
||||||
pThis->deepCountJob = NULL;
|
|
||||||
|
|
||||||
// stop the timer
|
|
||||||
if(pThis->fileSizeTimer) {
|
|
||||||
pThis->fileSizeTimer->stop();
|
|
||||||
delete pThis->fileSizeTimer;
|
|
||||||
pThis->fileSizeTimer = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FilePropsDialog::onFileSizeTimerTimeout() {
|
|
||||||
if(deepCountJob && !fm_job_is_cancelled(FM_JOB(deepCountJob))) {
|
|
||||||
char size_str[128];
|
|
||||||
fm_file_size_to_str(size_str, sizeof(size_str), deepCountJob->total_size,
|
|
||||||
fm_config->si_unit);
|
|
||||||
// FIXME:
|
|
||||||
// OMG! It's really unbelievable that Qt developers only implement
|
|
||||||
// QObject::tr(... int n). GNU gettext developers are smarter and
|
|
||||||
// they use unsigned long instead of int.
|
|
||||||
// We cannot use Qt here to handle plural forms. So sad. :-(
|
|
||||||
QString str = QString::fromUtf8(size_str) %
|
|
||||||
QString(" (%1 B)").arg(deepCountJob->total_size);
|
|
||||||
// tr(" (%n) byte(s)", "", deepCountJob->total_size);
|
|
||||||
ui->fileSize->setText(str);
|
|
||||||
|
|
||||||
fm_file_size_to_str(size_str, sizeof(size_str), deepCountJob->total_ondisk_size,
|
|
||||||
fm_config->si_unit);
|
|
||||||
str = QString::fromUtf8(size_str) %
|
|
||||||
QString(" (%1 B)").arg(deepCountJob->total_ondisk_size);
|
|
||||||
// tr(" (%n) byte(s)", "", deepCountJob->total_ondisk_size);
|
|
||||||
ui->onDiskSize->setText(str);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FilePropsDialog::accept() {
|
|
||||||
|
|
||||||
// applications
|
|
||||||
if(mimeType && ui->openWith->isChanged()) {
|
|
||||||
GAppInfo* currentApp = ui->openWith->selectedApp();
|
|
||||||
g_app_info_set_as_default_for_type(currentApp, fm_mime_type_get_type(mimeType), NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if chown or chmod is needed
|
|
||||||
guint32 newUid = uidFromName(ui->owner->text());
|
|
||||||
guint32 newGid = gidFromName(ui->ownerGroup->text());
|
|
||||||
bool needChown = (newUid != -1 && newUid != uid) || (newGid != -1 && newGid != gid);
|
|
||||||
|
|
||||||
int newOwnerPermSel = ui->ownerPerm->currentIndex();
|
|
||||||
int newGroupPermSel = ui->groupPerm->currentIndex();
|
|
||||||
int newOtherPermSel = ui->otherPerm->currentIndex();
|
|
||||||
Qt::CheckState newExecCheckState = ui->executable->checkState();
|
|
||||||
bool needChmod = ((newOwnerPermSel != ownerPermSel) ||
|
|
||||||
(newGroupPermSel != groupPermSel) ||
|
|
||||||
(newOtherPermSel != otherPermSel) ||
|
|
||||||
(newExecCheckState != execCheckState));
|
|
||||||
|
|
||||||
if(needChmod || needChown) {
|
|
||||||
FmPathList* paths = fm_path_list_new_from_file_info_list(fileInfos_);
|
|
||||||
FileOperation* op = new FileOperation(FileOperation::ChangeAttr, paths);
|
|
||||||
fm_path_list_unref(paths);
|
|
||||||
if(needChown) {
|
|
||||||
// don't do chown if new uid/gid and the original ones are actually the same.
|
|
||||||
if(newUid == uid)
|
|
||||||
newUid = -1;
|
|
||||||
if(newGid == gid)
|
|
||||||
newGid = -1;
|
|
||||||
op->setChown(newUid, newGid);
|
|
||||||
}
|
|
||||||
if(needChmod) {
|
|
||||||
mode_t newMode = 0;
|
|
||||||
mode_t newModeMask = 0;
|
|
||||||
// FIXME: we need to make sure that folders with "r" permission also have "x"
|
|
||||||
// at the same time. Otherwise, it's not able to browse the folder later.
|
|
||||||
if(newOwnerPermSel != ownerPermSel && newOwnerPermSel != ACCESS_NO_CHANGE) {
|
|
||||||
// owner permission changed
|
|
||||||
newModeMask |= (S_IRUSR|S_IWUSR); // affect user bits
|
|
||||||
if(newOwnerPermSel == ACCESS_READ_ONLY)
|
|
||||||
newMode |= S_IRUSR;
|
|
||||||
else if(newOwnerPermSel == ACCESS_READ_WRITE)
|
|
||||||
newMode |= (S_IRUSR|S_IWUSR);
|
|
||||||
}
|
|
||||||
if(newGroupPermSel != groupPermSel && newGroupPermSel != ACCESS_NO_CHANGE) {
|
|
||||||
qDebug("newGroupPermSel: %d", newGroupPermSel);
|
|
||||||
// group permission changed
|
|
||||||
newModeMask |= (S_IRGRP|S_IWGRP); // affect group bits
|
|
||||||
if(newGroupPermSel == ACCESS_READ_ONLY)
|
|
||||||
newMode |= S_IRGRP;
|
|
||||||
else if(newGroupPermSel == ACCESS_READ_WRITE)
|
|
||||||
newMode |= (S_IRGRP|S_IWGRP);
|
|
||||||
}
|
|
||||||
if(newOtherPermSel != otherPermSel && newOtherPermSel != ACCESS_NO_CHANGE) {
|
|
||||||
// other permission changed
|
|
||||||
newModeMask |= (S_IROTH|S_IWOTH); // affect other bits
|
|
||||||
if(newOtherPermSel == ACCESS_READ_ONLY)
|
|
||||||
newMode |= S_IROTH;
|
|
||||||
else if(newOtherPermSel == ACCESS_READ_WRITE)
|
|
||||||
newMode |= (S_IROTH|S_IWOTH);
|
|
||||||
}
|
|
||||||
if(newExecCheckState != execCheckState && newExecCheckState != Qt::PartiallyChecked) {
|
|
||||||
// executable state changed
|
|
||||||
newModeMask |= (S_IXUSR|S_IXGRP|S_IXOTH);
|
|
||||||
if(newExecCheckState == Qt::Checked)
|
|
||||||
newMode |= (S_IXUSR|S_IXGRP|S_IXOTH);
|
|
||||||
}
|
|
||||||
op->setChmod(newMode, newModeMask);
|
|
||||||
|
|
||||||
if(hasDir) { // if there are some dirs in our selected files
|
|
||||||
QMessageBox::StandardButton r = QMessageBox::question(this,
|
|
||||||
tr("Apply changes"),
|
|
||||||
tr("Do you want to recursively apply these changes to all files and sub-folders?"),
|
|
||||||
QMessageBox::Yes|QMessageBox::No);
|
|
||||||
if(r == QMessageBox::Yes)
|
|
||||||
op->setRecursiveChattr(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
op->setAutoDestroy(true);
|
|
||||||
op->run();
|
|
||||||
}
|
|
||||||
|
|
||||||
QDialog::accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FilePropsDialog::initOwner() {
|
|
||||||
if(allNative) {
|
|
||||||
if(uid != DIFFERENT_UIDS)
|
|
||||||
ui->owner->setText(uidToName(uid));
|
|
||||||
if(gid != DIFFERENT_GIDS)
|
|
||||||
ui->ownerGroup->setText(gidToName(gid));
|
|
||||||
|
|
||||||
if(geteuid() != 0) { // on local filesystems, only root can do chown.
|
|
||||||
ui->owner->setEnabled(false);
|
|
||||||
ui->ownerGroup->setEnabled(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,97 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef FM_FILEPROPSDIALOG_H
|
|
||||||
#define FM_FILEPROPSDIALOG_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QDialog>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
|
|
||||||
namespace Ui {
|
|
||||||
class FilePropsDialog;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class LIBFM_QT_API FilePropsDialog : public QDialog {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit FilePropsDialog(FmFileInfoList* files, QWidget* parent = 0, Qt::WindowFlags f = 0);
|
|
||||||
virtual ~FilePropsDialog();
|
|
||||||
|
|
||||||
virtual void accept();
|
|
||||||
|
|
||||||
static FilePropsDialog* showForFile(FmFileInfo* file, QWidget* parent = 0) {
|
|
||||||
FmFileInfoList* files = fm_file_info_list_new();
|
|
||||||
fm_file_info_list_push_tail(files, file);
|
|
||||||
FilePropsDialog* dlg = showForFiles(files, parent);
|
|
||||||
fm_file_info_list_unref(files);
|
|
||||||
return dlg;
|
|
||||||
}
|
|
||||||
|
|
||||||
static FilePropsDialog* showForFiles(FmFileInfoList* files, QWidget* parent = 0) {
|
|
||||||
FilePropsDialog* dlg = new FilePropsDialog(files, parent);
|
|
||||||
dlg->show();
|
|
||||||
return dlg;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void initGeneralPage();
|
|
||||||
void initApplications();
|
|
||||||
void initPermissionsPage();
|
|
||||||
void initOwner();
|
|
||||||
|
|
||||||
static void onDeepCountJobFinished(FmDeepCountJob* job, FilePropsDialog* pThis);
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void onFileSizeTimerTimeout();
|
|
||||||
|
|
||||||
private:
|
|
||||||
Ui::FilePropsDialog* ui;
|
|
||||||
FmFileInfoList* fileInfos_; // list of all file infos
|
|
||||||
FmFileInfo* fileInfo; // file info of the first file in the list
|
|
||||||
bool singleType; // all files are of the same type?
|
|
||||||
bool singleFile; // only one file is selected?
|
|
||||||
bool hasDir; // is there any dir in the files?
|
|
||||||
bool allNative; // all files are on native UNIX filesystems (not virtual or remote)
|
|
||||||
|
|
||||||
FmMimeType* mimeType; // mime type of the files
|
|
||||||
|
|
||||||
gint32 uid; // owner uid of the files, -1 means all files do not have the same uid
|
|
||||||
gint32 gid; // owner gid of the files, -1 means all files do not have the same uid
|
|
||||||
mode_t ownerPerm; // read permission of the files, -1 means not all files have the same value
|
|
||||||
int ownerPermSel;
|
|
||||||
mode_t groupPerm; // read permission of the files, -1 means not all files have the same value
|
|
||||||
int groupPermSel;
|
|
||||||
mode_t otherPerm; // read permission of the files, -1 means not all files have the same value
|
|
||||||
int otherPermSel;
|
|
||||||
mode_t execPerm; // exec permission of the files
|
|
||||||
Qt::CheckState execCheckState;
|
|
||||||
|
|
||||||
FmDeepCountJob* deepCountJob; // job used to count total size
|
|
||||||
QTimer* fileSizeTimer;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_FILEPROPSDIALOG_H
|
|
@ -1,449 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>SearchDialog</class>
|
|
||||||
<widget class="QDialog" name="SearchDialog">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>512</width>
|
|
||||||
<height>420</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Search Files</string>
|
|
||||||
</property>
|
|
||||||
<property name="windowIcon">
|
|
||||||
<iconset theme="system-search">
|
|
||||||
<normaloff/>
|
|
||||||
</iconset>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="QTabWidget" name="tabWidget">
|
|
||||||
<property name="currentIndex">
|
|
||||||
<number>0</number>
|
|
||||||
</property>
|
|
||||||
<widget class="QWidget" name="tab">
|
|
||||||
<attribute name="title">
|
|
||||||
<string>Name/Location</string>
|
|
||||||
</attribute>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,1">
|
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="groupBox">
|
|
||||||
<property name="title">
|
|
||||||
<string>File Name Patterns:</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="namePatterns">
|
|
||||||
<property name="text">
|
|
||||||
<string>*</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="nameCaseInsensitive">
|
|
||||||
<property name="text">
|
|
||||||
<string>Case insensitive</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="nameRegExp">
|
|
||||||
<property name="text">
|
|
||||||
<string>Use regular expression</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="groupBox_2">
|
|
||||||
<property name="title">
|
|
||||||
<string>Places to Search:</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="QListWidget" name="listView"/>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="addPath">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Add</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="list-add">
|
|
||||||
<normaloff/>
|
|
||||||
</iconset>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="removePath">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Remove</string>
|
|
||||||
</property>
|
|
||||||
<property name="icon">
|
|
||||||
<iconset theme="list-remove">
|
|
||||||
<normaloff/>
|
|
||||||
</iconset>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="verticalSpacer">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>40</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="recursiveSearch">
|
|
||||||
<property name="text">
|
|
||||||
<string>Search in sub directories</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="searchHidden">
|
|
||||||
<property name="text">
|
|
||||||
<string>Search for hidden files</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="tab_2">
|
|
||||||
<attribute name="title">
|
|
||||||
<string>File Type</string>
|
|
||||||
</attribute>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="groupBox_3">
|
|
||||||
<property name="title">
|
|
||||||
<string>Only search for files of following types:</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_6">
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="searchTextFiles">
|
|
||||||
<property name="text">
|
|
||||||
<string>Text files</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="searchImages">
|
|
||||||
<property name="text">
|
|
||||||
<string>Image files</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="searchAudio">
|
|
||||||
<property name="text">
|
|
||||||
<string>Audio files</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="searchVideo">
|
|
||||||
<property name="text">
|
|
||||||
<string>Video files</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="searchDocuments">
|
|
||||||
<property name="text">
|
|
||||||
<string>Documents</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="searchFolders">
|
|
||||||
<property name="text">
|
|
||||||
<string>Folders</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="verticalSpacer_2">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>40</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="tab_3">
|
|
||||||
<attribute name="title">
|
|
||||||
<string>Content</string>
|
|
||||||
</attribute>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_9">
|
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="groupBox_4">
|
|
||||||
<property name="title">
|
|
||||||
<string>File contains:</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_8">
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="contentPattern"/>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="contentCaseInsensitive">
|
|
||||||
<property name="text">
|
|
||||||
<string>Case insensiti&ve</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="contentRegExp">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Use regular expression</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="verticalSpacer_3">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>186</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<widget class="QWidget" name="tab_4">
|
|
||||||
<attribute name="title">
|
|
||||||
<string>Properties</string>
|
|
||||||
</attribute>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout_10">
|
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="groupBox_5">
|
|
||||||
<property name="title">
|
|
||||||
<string>File Size:</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QFormLayout" name="formLayout">
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QCheckBox" name="largerThan">
|
|
||||||
<property name="text">
|
|
||||||
<string>Larger than:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1">
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
|
||||||
<item>
|
|
||||||
<widget class="QSpinBox" name="minSize"/>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QComboBox" name="minSizeUnit">
|
|
||||||
<property name="currentIndex">
|
|
||||||
<number>2</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Bytes</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>KiB</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>MiB</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>GiB</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QCheckBox" name="smallerThan">
|
|
||||||
<property name="text">
|
|
||||||
<string>Smaller than:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
|
||||||
<item>
|
|
||||||
<widget class="QSpinBox" name="maxSize"/>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QComboBox" name="maxSizeUnit">
|
|
||||||
<property name="currentIndex">
|
|
||||||
<number>2</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>Bytes</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>KiB</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>MiB</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<property name="text">
|
|
||||||
<string>GiB</string>
|
|
||||||
</property>
|
|
||||||
</item>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QGroupBox" name="groupBox_6">
|
|
||||||
<property name="title">
|
|
||||||
<string>Last Modified Time:</string>
|
|
||||||
</property>
|
|
||||||
<layout class="QFormLayout" name="formLayout_2">
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QCheckBox" name="earlierThan">
|
|
||||||
<property name="text">
|
|
||||||
<string>Earlier than:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0">
|
|
||||||
<widget class="QCheckBox" name="laterThan">
|
|
||||||
<property name="text">
|
|
||||||
<string>Later than:</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QDateEdit" name="maxTime">
|
|
||||||
<property name="calendarPopup">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="1">
|
|
||||||
<widget class="QDateEdit" name="minTime">
|
|
||||||
<property name="calendarPopup">
|
|
||||||
<bool>true</bool>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="verticalSpacer_4">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>40</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="standardButtons">
|
|
||||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections>
|
|
||||||
<connection>
|
|
||||||
<sender>buttonBox</sender>
|
|
||||||
<signal>accepted()</signal>
|
|
||||||
<receiver>SearchDialog</receiver>
|
|
||||||
<slot>accept()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>248</x>
|
|
||||||
<y>254</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>157</x>
|
|
||||||
<y>274</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
<connection>
|
|
||||||
<sender>buttonBox</sender>
|
|
||||||
<signal>rejected()</signal>
|
|
||||||
<receiver>SearchDialog</receiver>
|
|
||||||
<slot>reject()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>316</x>
|
|
||||||
<y>260</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>286</x>
|
|
||||||
<y>274</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
</connections>
|
|
||||||
</ui>
|
|
@ -1,143 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with this library; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "filesearchdialog.h"
|
|
||||||
#include <QMessageBox>
|
|
||||||
#include "fm-search.h"
|
|
||||||
#include "ui_filesearch.h"
|
|
||||||
#include <limits>
|
|
||||||
#include <QFileDialog>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
FileSearchDialog::FileSearchDialog(QStringList paths, QWidget* parent, Qt::WindowFlags f):
|
|
||||||
QDialog(parent, f),
|
|
||||||
ui(new Ui::SearchDialog()) {
|
|
||||||
ui->setupUi(this);
|
|
||||||
ui->minSize->setMaximum(std::numeric_limits<int>().max());
|
|
||||||
ui->maxSize->setMaximum(std::numeric_limits<int>().max());
|
|
||||||
Q_FOREACH(const QString& path, paths) {
|
|
||||||
ui->listView->addItem(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
ui->maxTime->setDate(QDate::currentDate());
|
|
||||||
ui->minTime->setDate(QDate::currentDate());
|
|
||||||
|
|
||||||
connect(ui->addPath, &QPushButton::clicked, this, &FileSearchDialog::onAddPath);
|
|
||||||
connect(ui->removePath, &QPushButton::clicked, this, &FileSearchDialog::onRemovePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
FileSearchDialog::~FileSearchDialog() {
|
|
||||||
delete ui;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileSearchDialog::accept() {
|
|
||||||
// build the search:/// uri
|
|
||||||
int n = ui->listView->count();
|
|
||||||
if(n > 0) {
|
|
||||||
FmSearch* search = fm_search_new();
|
|
||||||
int i;
|
|
||||||
for(i = 0; i < n; ++i) { // add directories
|
|
||||||
QListWidgetItem* item = ui->listView->item(i);
|
|
||||||
fm_search_add_dir(search, item->text().toLocal8Bit().constData());
|
|
||||||
}
|
|
||||||
|
|
||||||
fm_search_set_recursive(search, ui->recursiveSearch->isChecked());
|
|
||||||
fm_search_set_show_hidden(search, ui->searchHidden->isChecked());
|
|
||||||
fm_search_set_name_patterns(search, ui->namePatterns->text().toUtf8().constData());
|
|
||||||
fm_search_set_name_ci(search, ui->nameCaseInsensitive->isChecked());
|
|
||||||
fm_search_set_name_regex(search, ui->nameRegExp->isChecked());
|
|
||||||
|
|
||||||
fm_search_set_content_pattern(search, ui->contentPattern->text().toUtf8().constData());
|
|
||||||
fm_search_set_content_ci(search, ui->contentCaseInsensitive->isChecked());
|
|
||||||
fm_search_set_content_regex(search, ui->contentRegExp->isChecked());
|
|
||||||
|
|
||||||
// search for the files of specific mime-types
|
|
||||||
if(ui->searchTextFiles->isChecked())
|
|
||||||
fm_search_add_mime_type(search, "text/plain");
|
|
||||||
if(ui->searchImages->isChecked())
|
|
||||||
fm_search_add_mime_type(search, "image/*");
|
|
||||||
if(ui->searchAudio->isChecked())
|
|
||||||
fm_search_add_mime_type(search, "audio/*");
|
|
||||||
if(ui->searchVideo->isChecked())
|
|
||||||
fm_search_add_mime_type(search, "video/*");
|
|
||||||
if(ui->searchFolders->isChecked())
|
|
||||||
fm_search_add_mime_type(search, "inode/directory");
|
|
||||||
if(ui->searchDocuments->isChecked()) {
|
|
||||||
const char* doc_types[] = {
|
|
||||||
"application/pdf",
|
|
||||||
/* "text/html;" */
|
|
||||||
"application/vnd.oasis.opendocument.*",
|
|
||||||
"application/vnd.openxmlformats-officedocument.*",
|
|
||||||
"application/msword;application/vnd.ms-word",
|
|
||||||
"application/msexcel;application/vnd.ms-excel"
|
|
||||||
};
|
|
||||||
for(i = 0; i < sizeof(doc_types)/sizeof(char*); ++i)
|
|
||||||
fm_search_add_mime_type(search, doc_types[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// search based on file size
|
|
||||||
const unsigned int unit_bytes[] = {1, (1024), (1024*1024), (1024*1024*1024)};
|
|
||||||
if(ui->largerThan->isChecked()) {
|
|
||||||
guint64 size = ui->minSize->value() * unit_bytes[ui->minSizeUnit->currentIndex()];
|
|
||||||
fm_search_set_min_size(search, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ui->smallerThan->isChecked()) {
|
|
||||||
guint64 size = ui->maxSize->value() * unit_bytes[ui->maxSizeUnit->currentIndex()];
|
|
||||||
fm_search_set_min_size(search, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// search based on file mtime (we only support date in YYYY-MM-DD format)
|
|
||||||
if(ui->earlierThan->isChecked()) {
|
|
||||||
fm_search_set_max_mtime(search, ui->maxTime->date().toString(QStringLiteral("yyyy-MM-dd")).toUtf8().constData());
|
|
||||||
}
|
|
||||||
if(ui->laterThan->isChecked()) {
|
|
||||||
fm_search_set_min_mtime(search, ui->minTime->date().toString(QStringLiteral("yyyy-MM-dd")).toUtf8().constData());
|
|
||||||
}
|
|
||||||
|
|
||||||
searchUri_.take(fm_search_dup_path(search));
|
|
||||||
|
|
||||||
fm_search_free(search);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
QMessageBox::critical(this, tr("Error"), tr("You should add at least add one directory to search."));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
QDialog::accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileSearchDialog::onAddPath() {
|
|
||||||
QString dir = QFileDialog::getExistingDirectory(this, tr("Select a folder"));
|
|
||||||
if(dir.isEmpty())
|
|
||||||
return;
|
|
||||||
// avoid adding duplicated items
|
|
||||||
if(ui->listView->findItems(dir, Qt::MatchFixedString|Qt::MatchCaseSensitive).isEmpty()) {
|
|
||||||
ui->listView->addItem(dir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FileSearchDialog::onRemovePath() {
|
|
||||||
// remove selected items
|
|
||||||
Q_FOREACH(QListWidgetItem* item, ui->listView->selectedItems()) {
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2015 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
*
|
|
||||||
* This library is free software; you can redistribute it and/or
|
|
||||||
* modify it under the terms of the GNU Lesser General Public
|
|
||||||
* License as published by the Free Software Foundation; either
|
|
||||||
* version 2.1 of the License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This library is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
* Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public
|
|
||||||
* License along with this library; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FM_FILESEARCHDIALOG_H
|
|
||||||
#define FM_FILESEARCHDIALOG_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QDialog>
|
|
||||||
#include "path.h"
|
|
||||||
|
|
||||||
namespace Ui {
|
|
||||||
class SearchDialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class LIBFM_QT_API FileSearchDialog : public QDialog
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FileSearchDialog(QStringList paths = QStringList(), QWidget * parent = 0, Qt::WindowFlags f = 0);
|
|
||||||
~FileSearchDialog();
|
|
||||||
|
|
||||||
Path searchUri() const {
|
|
||||||
return searchUri_;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void accept();
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void onAddPath();
|
|
||||||
void onRemovePath();
|
|
||||||
|
|
||||||
private:
|
|
||||||
Ui::SearchDialog* ui;
|
|
||||||
Path searchUri_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_FILESEARCHDIALOG_H
|
|
@ -1,317 +0,0 @@
|
|||||||
/*
|
|
||||||
* fm-search-uri.c
|
|
||||||
*
|
|
||||||
* Copyright 2015 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
* Copyright 2012-2014 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "fm-search.h"
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
struct _FmSearch
|
|
||||||
{
|
|
||||||
gboolean recursive;
|
|
||||||
gboolean show_hidden;
|
|
||||||
char* name_patterns;
|
|
||||||
gboolean name_ci;
|
|
||||||
gboolean name_regex;
|
|
||||||
char* content_pattern;
|
|
||||||
gboolean content_ci;
|
|
||||||
gboolean content_regex;
|
|
||||||
GList* mime_types;
|
|
||||||
GList* search_path_list;
|
|
||||||
guint64 max_size;
|
|
||||||
guint64 min_size;
|
|
||||||
char* max_mtime;
|
|
||||||
char* min_mtime;
|
|
||||||
};
|
|
||||||
|
|
||||||
FmSearch* fm_search_new (void)
|
|
||||||
{
|
|
||||||
FmSearch* search = (FmSearch*)g_slice_new0(FmSearch);
|
|
||||||
return search;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fm_search_free(FmSearch* search)
|
|
||||||
{
|
|
||||||
g_list_free_full(search->mime_types, (GDestroyNotify)g_free);
|
|
||||||
g_list_free_full(search->search_path_list, (GDestroyNotify)g_free);
|
|
||||||
g_free(search->name_patterns);
|
|
||||||
g_free(search->content_pattern);
|
|
||||||
g_free(search->max_mtime);
|
|
||||||
g_free(search->min_mtime);
|
|
||||||
g_slice_free(FmSearch, search);
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean fm_search_get_recursive(FmSearch* search)
|
|
||||||
{
|
|
||||||
return search->recursive;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fm_search_set_recursive(FmSearch* search, gboolean recursive)
|
|
||||||
{
|
|
||||||
search->recursive = recursive;
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean fm_search_get_show_hidden(FmSearch* search)
|
|
||||||
{
|
|
||||||
return search->show_hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fm_search_set_show_hidden(FmSearch* search, gboolean show_hidden)
|
|
||||||
{
|
|
||||||
search->show_hidden = show_hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* fm_search_get_name_patterns(FmSearch* search)
|
|
||||||
{
|
|
||||||
return search->name_patterns;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fm_search_set_name_patterns(FmSearch* search, const char* name_patterns)
|
|
||||||
{
|
|
||||||
g_free(search->name_patterns);
|
|
||||||
search->name_patterns = g_strdup(name_patterns);
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean fm_search_get_name_ci(FmSearch* search)
|
|
||||||
{
|
|
||||||
return search->name_ci;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fm_search_set_name_ci(FmSearch* search, gboolean name_ci)
|
|
||||||
{
|
|
||||||
search->name_ci = name_ci;
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean fm_search_get_name_regex(FmSearch* search)
|
|
||||||
{
|
|
||||||
return search->name_regex;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fm_search_set_name_regex(FmSearch* search, gboolean name_regex)
|
|
||||||
{
|
|
||||||
search->name_regex = name_regex;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* fm_search_get_content_pattern(FmSearch* search)
|
|
||||||
{
|
|
||||||
return search->content_pattern;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fm_search_set_content_pattern(FmSearch* search, const char* content_pattern)
|
|
||||||
{
|
|
||||||
g_free(search->content_pattern);
|
|
||||||
search->content_pattern = g_strdup(content_pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean fm_search_get_content_ci(FmSearch* search)
|
|
||||||
{
|
|
||||||
return search->content_ci;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fm_search_set_content_ci(FmSearch* search, gboolean content_ci)
|
|
||||||
{
|
|
||||||
search->content_ci = content_ci;
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean fm_search_get_content_regex(FmSearch* search)
|
|
||||||
{
|
|
||||||
return search->content_regex;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fm_search_set_content_regex(FmSearch* search, gboolean content_regex)
|
|
||||||
{
|
|
||||||
search->content_regex = content_regex;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fm_search_add_dir(FmSearch* search, const char* dir)
|
|
||||||
{
|
|
||||||
GList* l = g_list_find_custom(search->search_path_list, dir, (GCompareFunc)strcmp);
|
|
||||||
if(!l)
|
|
||||||
search->search_path_list = g_list_prepend(search->search_path_list, g_strdup(dir));
|
|
||||||
}
|
|
||||||
|
|
||||||
void fm_search_remove_dir(FmSearch* search, const char* dir)
|
|
||||||
{
|
|
||||||
GList* l = g_list_find_custom(search->search_path_list, dir, (GCompareFunc)strcmp);
|
|
||||||
if(G_LIKELY(l))
|
|
||||||
{
|
|
||||||
g_free(l->data);
|
|
||||||
search->search_path_list = g_list_delete_link(search->search_path_list, l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GList* fm_search_get_dirs(FmSearch* search)
|
|
||||||
{
|
|
||||||
return search->search_path_list;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fm_search_add_mime_type(FmSearch* search, const char* mime_type)
|
|
||||||
{
|
|
||||||
GList* l = g_list_find_custom(search->mime_types, mime_type, (GCompareFunc)strcmp);
|
|
||||||
if(!l)
|
|
||||||
search->mime_types = g_list_prepend(search->mime_types, g_strdup(mime_type));
|
|
||||||
}
|
|
||||||
|
|
||||||
void fm_search_remove_mime_type(FmSearch* search, const char* mime_type)
|
|
||||||
{
|
|
||||||
GList* l = g_list_find_custom(search->mime_types, mime_type, (GCompareFunc)strcmp);
|
|
||||||
if(G_LIKELY(l))
|
|
||||||
{
|
|
||||||
g_free(l->data);
|
|
||||||
search->mime_types = g_list_delete_link(search->mime_types, l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GList* fm_search_get_mime_types(FmSearch* search)
|
|
||||||
{
|
|
||||||
return search->mime_types;
|
|
||||||
}
|
|
||||||
|
|
||||||
guint64 fm_search_get_max_size(FmSearch* search)
|
|
||||||
{
|
|
||||||
return search->max_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fm_search_set_max_size(FmSearch* search, guint64 size)
|
|
||||||
{
|
|
||||||
search->max_size = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
guint64 fm_search_get_min_size(FmSearch* search)
|
|
||||||
{
|
|
||||||
return search->min_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fm_search_set_min_size(FmSearch* search, guint64 size)
|
|
||||||
{
|
|
||||||
search->min_size = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* format of mtime: YYYY-MM-DD */
|
|
||||||
const char* fm_search_get_max_mtime(FmSearch* search)
|
|
||||||
{
|
|
||||||
return search->max_mtime;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fm_search_set_max_mtime(FmSearch* search, const char* mtime)
|
|
||||||
{
|
|
||||||
g_free(search->max_mtime);
|
|
||||||
search->max_mtime = g_strdup(mtime);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* format of mtime: YYYY-MM-DD */
|
|
||||||
const char* fm_search_get_min_mtime(FmSearch* search)
|
|
||||||
{
|
|
||||||
return search->min_mtime;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fm_search_set_min_mtime(FmSearch* search, const char* mtime)
|
|
||||||
{
|
|
||||||
g_free(search->min_mtime);
|
|
||||||
search->min_mtime = g_strdup(mtime);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* really build the path */
|
|
||||||
FmPath* fm_search_dup_path(FmSearch* search)
|
|
||||||
{
|
|
||||||
FmPath* search_path = NULL;
|
|
||||||
GString* search_str = g_string_sized_new(1024);
|
|
||||||
/* build the search:// URI to perform the search */
|
|
||||||
g_string_append(search_str, "search://");
|
|
||||||
|
|
||||||
if(search->search_path_list) /* we need to have at least one dir path */
|
|
||||||
{
|
|
||||||
char *escaped;
|
|
||||||
/* add paths */
|
|
||||||
GList* l;
|
|
||||||
for(l = search->search_path_list; ; )
|
|
||||||
{
|
|
||||||
char *path_str = (char*)l->data;
|
|
||||||
/* escape possible '?' and ',' */
|
|
||||||
escaped = g_uri_escape_string(path_str, "!$&'()*+:;=/@", TRUE);
|
|
||||||
g_string_append(search_str, escaped);
|
|
||||||
g_free(escaped);
|
|
||||||
|
|
||||||
l = l->next;
|
|
||||||
if(!l) /* no more items */
|
|
||||||
break;
|
|
||||||
g_string_append_c(search_str, ','); /* separator for paths */
|
|
||||||
}
|
|
||||||
|
|
||||||
g_string_append_c(search_str, '?');
|
|
||||||
g_string_append_printf(search_str, "recursive=%c", search->recursive ? '1' : '0');
|
|
||||||
g_string_append_printf(search_str, "&show_hidden=%c", search->show_hidden ? '1' : '0');
|
|
||||||
if(search->name_patterns && *search->name_patterns)
|
|
||||||
{
|
|
||||||
/* escape ampersands in pattern */
|
|
||||||
escaped = g_uri_escape_string(search->name_patterns, ":/?#[]@!$'()*+,;", TRUE);
|
|
||||||
if(search->name_regex)
|
|
||||||
g_string_append_printf(search_str, "&name_regex=%s", escaped);
|
|
||||||
else
|
|
||||||
g_string_append_printf(search_str, "&name=%s", escaped);
|
|
||||||
if(search->name_ci)
|
|
||||||
g_string_append_printf(search_str, "&name_ci=%c", search->name_ci ? '1' : '0');
|
|
||||||
g_free(escaped);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(search->content_pattern && *search->content_pattern)
|
|
||||||
{
|
|
||||||
/* escape ampersands in pattern */
|
|
||||||
escaped = g_uri_escape_string(search->content_pattern, ":/?#[]@!$'()*+,;^<>{}", TRUE);
|
|
||||||
if(search->content_regex)
|
|
||||||
g_string_append_printf(search_str, "&content_regex=%s", escaped);
|
|
||||||
else
|
|
||||||
g_string_append_printf(search_str, "&content=%s", escaped);
|
|
||||||
g_free(escaped);
|
|
||||||
if(search->content_ci)
|
|
||||||
g_string_append_printf(search_str, "&content_ci=%c", search->content_ci ? '1' : '0');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* search for the files of specific mime-types */
|
|
||||||
if(search->mime_types)
|
|
||||||
{
|
|
||||||
GList* l;
|
|
||||||
g_string_append(search_str, "&mime_types=");
|
|
||||||
for(l = search->mime_types; l; l=l->next)
|
|
||||||
{
|
|
||||||
const char* mime_type = (const char*)l->data;
|
|
||||||
g_string_append(search_str, mime_type);
|
|
||||||
if(l->next)
|
|
||||||
g_string_append_c(search_str, ';');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(search->min_size)
|
|
||||||
g_string_append_printf(search_str, "&min_size=%llu", search->min_size);
|
|
||||||
|
|
||||||
if(search->max_size)
|
|
||||||
g_string_append_printf(search_str, "&max_size=%llu", search->max_size);
|
|
||||||
|
|
||||||
if(search->min_mtime)
|
|
||||||
g_string_append_printf(search_str, "&min_mtime=%s", search->min_mtime);
|
|
||||||
|
|
||||||
if(search->max_mtime)
|
|
||||||
g_string_append_printf(search_str, "&max_mtime=%s", search->max_mtime);
|
|
||||||
|
|
||||||
search_path = fm_path_new_for_uri(search_str->str);
|
|
||||||
g_string_free(search_str, TRUE);
|
|
||||||
}
|
|
||||||
return search_path;
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
/*
|
|
||||||
* fm-search-uri.h
|
|
||||||
*
|
|
||||||
* Copyright 2015 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program; if not, write to the Free Software
|
|
||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
||||||
* MA 02110-1301, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* FmSearch implements a tool used to generate a search:// URI used by libfm to search for files.
|
|
||||||
* This API might become part of libfm in the future.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _FM_SEARCH_H_
|
|
||||||
#define _FM_SEARCH_H_
|
|
||||||
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
|
||||||
|
|
||||||
typedef struct _FmSearch FmSearch;
|
|
||||||
|
|
||||||
FmSearch* fm_search_new(void);
|
|
||||||
void fm_search_free(FmSearch* search);
|
|
||||||
|
|
||||||
FmPath* fm_search_dup_path(FmSearch* search);
|
|
||||||
|
|
||||||
gboolean fm_search_get_recursive(FmSearch* search);
|
|
||||||
void fm_search_set_recursive(FmSearch* search, gboolean recursive);
|
|
||||||
|
|
||||||
gboolean fm_search_get_show_hidden(FmSearch* search);
|
|
||||||
void fm_search_set_show_hidden(FmSearch* search, gboolean show_hidden);
|
|
||||||
|
|
||||||
const char* fm_search_get_name_patterns(FmSearch* search);
|
|
||||||
void fm_search_set_name_patterns(FmSearch* search, const char* name_patterns);
|
|
||||||
|
|
||||||
gboolean fm_search_get_name_ci(FmSearch* search);
|
|
||||||
void fm_search_set_name_ci(FmSearch* search, gboolean name_ci);
|
|
||||||
|
|
||||||
gboolean fm_search_get_name_regex(FmSearch* search);
|
|
||||||
void fm_search_set_name_regex(FmSearch* search, gboolean name_regex);
|
|
||||||
|
|
||||||
const char* fm_search_get_content_pattern(FmSearch* search);
|
|
||||||
void fm_search_set_content_pattern(FmSearch* search, const char* content_pattern);
|
|
||||||
|
|
||||||
gboolean fm_search_get_content_ci(FmSearch* search);
|
|
||||||
void fm_search_set_content_ci(FmSearch* search, gboolean content_ci);
|
|
||||||
|
|
||||||
gboolean fm_search_get_content_regex(FmSearch* search);
|
|
||||||
void fm_search_set_content_regex(FmSearch* search, gboolean content_regex);
|
|
||||||
|
|
||||||
void fm_search_add_dir(FmSearch* search, const char* dir);
|
|
||||||
void fm_search_remove_dir(FmSearch* search, const char* dir);
|
|
||||||
GList* fm_search_get_dirs(FmSearch* search);
|
|
||||||
|
|
||||||
void fm_search_add_mime_type(FmSearch* search, const char* mime_type);
|
|
||||||
void fm_search_remove_mime_type(FmSearch* search, const char* mime_type);
|
|
||||||
GList* fm_search_get_mime_types(FmSearch* search);
|
|
||||||
|
|
||||||
guint64 fm_search_get_max_size(FmSearch* search);
|
|
||||||
void fm_search_set_max_size(FmSearch* search, guint64 size);
|
|
||||||
|
|
||||||
guint64 fm_search_get_min_size(FmSearch* search);
|
|
||||||
void fm_search_set_min_size(FmSearch* search, guint64 size);
|
|
||||||
|
|
||||||
/* format of mtime: YYYY-MM-DD */
|
|
||||||
const char* fm_search_get_max_mtime(FmSearch* search);
|
|
||||||
void fm_search_set_max_mtime(FmSearch* search, const char* mtime);
|
|
||||||
|
|
||||||
/* format of mtime: YYYY-MM-DD */
|
|
||||||
const char* fm_search_get_min_mtime(FmSearch* search);
|
|
||||||
void fm_search_set_min_mtime(FmSearch* search, const char* mtime);
|
|
||||||
|
|
||||||
G_END_DECLS
|
|
||||||
|
|
||||||
#endif /* _FM_SEARCH_H_ */
|
|
@ -1,216 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "folderitemdelegate.h"
|
|
||||||
#include "foldermodel.h"
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QModelIndex>
|
|
||||||
#include <QStyleOptionViewItemV4>
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QIcon>
|
|
||||||
#include <QTextLayout>
|
|
||||||
#include <QTextOption>
|
|
||||||
#include <QTextLine>
|
|
||||||
#include <QDebug>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
FolderItemDelegate::FolderItemDelegate(QAbstractItemView* view, QObject* parent):
|
|
||||||
QStyledItemDelegate(parent ? parent : view),
|
|
||||||
symlinkIcon_(QIcon::fromTheme("emblem-symbolic-link")),
|
|
||||||
view_(view) {
|
|
||||||
}
|
|
||||||
|
|
||||||
FolderItemDelegate::~FolderItemDelegate() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
QSize FolderItemDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const {
|
|
||||||
QVariant value = index.data(Qt::SizeHintRole);
|
|
||||||
if(value.isValid())
|
|
||||||
return qvariant_cast<QSize>(value);
|
|
||||||
if(option.decorationPosition == QStyleOptionViewItem::Top ||
|
|
||||||
option.decorationPosition == QStyleOptionViewItem::Bottom) {
|
|
||||||
|
|
||||||
QStyleOptionViewItemV4 opt = option;
|
|
||||||
initStyleOption(&opt, index);
|
|
||||||
opt.decorationAlignment = Qt::AlignHCenter|Qt::AlignTop;
|
|
||||||
opt.displayAlignment = Qt::AlignTop|Qt::AlignHCenter;
|
|
||||||
|
|
||||||
// FIXME: there're some problems in this size hint calculation.
|
|
||||||
Q_ASSERT(gridSize_ != QSize());
|
|
||||||
QRectF textRect(0, 0, gridSize_.width() - 4, gridSize_.height() - opt.decorationSize.height() - 4);
|
|
||||||
drawText(NULL, opt, textRect); // passing NULL for painter will calculate the bounding rect only.
|
|
||||||
int width = qMax((int)textRect.width(), opt.decorationSize.width()) + 4;
|
|
||||||
int height = opt.decorationSize.height() + textRect.height() + 4;
|
|
||||||
return QSize(width, height);
|
|
||||||
}
|
|
||||||
return QStyledItemDelegate::sizeHint(option, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
QIcon::Mode FolderItemDelegate::iconModeFromState(QStyle::State state) {
|
|
||||||
QIcon::Mode iconMode;
|
|
||||||
if(state & QStyle::State_Enabled) {
|
|
||||||
if(state & QStyle::State_Selected)
|
|
||||||
iconMode = QIcon::Selected;
|
|
||||||
else {
|
|
||||||
iconMode = QIcon::Normal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
iconMode = QIcon::Disabled;
|
|
||||||
return iconMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
// special thanks to Razor-qt developer Alec Moskvin(amoskvin) for providing the fix!
|
|
||||||
void FolderItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const {
|
|
||||||
Q_ASSERT(index.isValid());
|
|
||||||
FmFileInfo* file = static_cast<FmFileInfo*>(index.data(FolderModel::FileInfoRole).value<void*>());
|
|
||||||
bool isSymlink = file && fm_file_info_is_symlink(file);
|
|
||||||
|
|
||||||
if(option.decorationPosition == QStyleOptionViewItem::Top ||
|
|
||||||
option.decorationPosition == QStyleOptionViewItem::Bottom) {
|
|
||||||
painter->save();
|
|
||||||
painter->setClipRect(option.rect);
|
|
||||||
|
|
||||||
QStyleOptionViewItemV4 opt = option;
|
|
||||||
initStyleOption(&opt, index);
|
|
||||||
opt.decorationAlignment = Qt::AlignHCenter|Qt::AlignTop;
|
|
||||||
opt.displayAlignment = Qt::AlignTop|Qt::AlignHCenter;
|
|
||||||
|
|
||||||
// draw the icon
|
|
||||||
QIcon::Mode iconMode = iconModeFromState(opt.state);
|
|
||||||
QPoint iconPos(opt.rect.x() + (opt.rect.width() - opt.decorationSize.width()) / 2, opt.rect.y());
|
|
||||||
QPixmap pixmap = opt.icon.pixmap(opt.decorationSize, iconMode);
|
|
||||||
painter->drawPixmap(iconPos, pixmap);
|
|
||||||
|
|
||||||
// draw some emblems for the item if needed
|
|
||||||
// we only support symlink emblem at the moment
|
|
||||||
if(isSymlink)
|
|
||||||
painter->drawPixmap(iconPos, symlinkIcon_.pixmap(opt.decorationSize / 2, iconMode));
|
|
||||||
|
|
||||||
// draw the text
|
|
||||||
QRectF textRect(opt.rect.x(), opt.rect.y() + opt.decorationSize.height(), opt.rect.width(), opt.rect.height() - opt.decorationSize.height());
|
|
||||||
drawText(painter, opt, textRect);
|
|
||||||
painter->restore();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// let QStyledItemDelegate does its default painting
|
|
||||||
QStyledItemDelegate::paint(painter, option, index);
|
|
||||||
|
|
||||||
// draw emblems if needed
|
|
||||||
if(isSymlink) {
|
|
||||||
QStyleOptionViewItemV4 opt = option;
|
|
||||||
initStyleOption(&opt, index);
|
|
||||||
QIcon::Mode iconMode = iconModeFromState(opt.state);
|
|
||||||
QPoint iconPos(opt.rect.x(), opt.rect.y() + (opt.rect.height() - opt.decorationSize.height()) / 2);
|
|
||||||
// draw some emblems for the item if needed
|
|
||||||
// we only support symlink emblem at the moment
|
|
||||||
painter->drawPixmap(iconPos, symlinkIcon_.pixmap(opt.decorationSize / 2, iconMode));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if painter is NULL, the method calculate the bounding rectangle of the text and save it to textRect
|
|
||||||
void FolderItemDelegate::drawText(QPainter* painter, QStyleOptionViewItemV4& opt, QRectF& textRect) const {
|
|
||||||
QTextLayout layout(opt.text, opt.font);
|
|
||||||
QTextOption textOption;
|
|
||||||
textOption.setAlignment(opt.displayAlignment);
|
|
||||||
textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
|
|
||||||
textOption.setTextDirection(opt.direction);
|
|
||||||
layout.setTextOption(textOption);
|
|
||||||
qreal height = 0;
|
|
||||||
qreal width = 0;
|
|
||||||
int visibleLines = 0;
|
|
||||||
layout.beginLayout();
|
|
||||||
QString elidedText;
|
|
||||||
for(;;) {
|
|
||||||
QTextLine line = layout.createLine();
|
|
||||||
if(!line.isValid())
|
|
||||||
break;
|
|
||||||
line.setLineWidth(textRect.width());
|
|
||||||
height += opt.fontMetrics.leading();
|
|
||||||
line.setPosition(QPointF(0, height));
|
|
||||||
if((height + line.height() + textRect.y()) > textRect.bottom()) {
|
|
||||||
// if part of this line falls outside the textRect, ignore it and quit.
|
|
||||||
QTextLine lastLine = layout.lineAt(visibleLines - 1);
|
|
||||||
elidedText = opt.text.mid(lastLine.textStart());
|
|
||||||
elidedText = opt.fontMetrics.elidedText(elidedText, opt.textElideMode, textRect.width());
|
|
||||||
if(visibleLines == 1) // this is the only visible line
|
|
||||||
width = textRect.width();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
height += line.height();
|
|
||||||
width = qMax(width, line.naturalTextWidth());
|
|
||||||
++ visibleLines;
|
|
||||||
}
|
|
||||||
layout.endLayout();
|
|
||||||
|
|
||||||
// draw background for selected item
|
|
||||||
QRectF boundRect = layout.boundingRect();
|
|
||||||
//qDebug() << "bound rect: " << boundRect << "width: " << width;
|
|
||||||
boundRect.setWidth(width);
|
|
||||||
boundRect.moveTo(textRect.x() + (textRect.width() - width)/2, textRect.y());
|
|
||||||
|
|
||||||
if(!painter) { // no painter, calculate the bounding rect only
|
|
||||||
textRect = boundRect;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled;
|
|
||||||
if(opt.state & QStyle::State_Selected) {
|
|
||||||
painter->fillRect(boundRect, opt.palette.highlight());
|
|
||||||
painter->setPen(opt.palette.color(cg, QPalette::HighlightedText));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
painter->setPen(opt.palette.color(cg, QPalette::Text));
|
|
||||||
|
|
||||||
// draw text
|
|
||||||
for(int i = 0; i < visibleLines; ++i) {
|
|
||||||
QTextLine line = layout.lineAt(i);
|
|
||||||
if(i == (visibleLines - 1) && !elidedText.isEmpty()) { // the last line, draw elided text
|
|
||||||
QPointF pos(textRect.x() + line.position().x(), textRect.y() + line.y() + line.ascent());
|
|
||||||
painter->drawText(pos, elidedText);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
line.draw(painter, textRect.topLeft());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(opt.state & QStyle::State_HasFocus) {
|
|
||||||
// draw focus rect
|
|
||||||
QStyleOptionFocusRect o;
|
|
||||||
o.QStyleOption::operator=(opt);
|
|
||||||
o.rect = boundRect.toRect(); // subElementRect(SE_ItemViewItemFocusRect, vopt, widget);
|
|
||||||
o.state |= QStyle::State_KeyboardFocusChange;
|
|
||||||
o.state |= QStyle::State_Item;
|
|
||||||
QPalette::ColorGroup cg = (opt.state & QStyle::State_Enabled)
|
|
||||||
? QPalette::Normal : QPalette::Disabled;
|
|
||||||
o.backgroundColor = opt.palette.color(cg, (opt.state & QStyle::State_Selected)
|
|
||||||
? QPalette::Highlight : QPalette::Window);
|
|
||||||
if (const QWidget* widget = opt.widget) {
|
|
||||||
QStyle* style = widget->style() ? widget->style() : qApp->style();
|
|
||||||
style->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter, widget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace Fm
|
|
@ -1,59 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef FM_FOLDERITEMDELEGATE_H
|
|
||||||
#define FM_FOLDERITEMDELEGATE_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QStyledItemDelegate>
|
|
||||||
#include <QAbstractItemView>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class LIBFM_QT_API FolderItemDelegate : public QStyledItemDelegate {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit FolderItemDelegate(QAbstractItemView* view, QObject* parent = 0);
|
|
||||||
virtual ~FolderItemDelegate();
|
|
||||||
|
|
||||||
void setGridSize(QSize size) {
|
|
||||||
gridSize_ = size;
|
|
||||||
}
|
|
||||||
|
|
||||||
QSize gridSize() {
|
|
||||||
return gridSize_;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index) const;
|
|
||||||
virtual void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void drawText(QPainter* painter, QStyleOptionViewItemV4& opt, QRectF& textRect) const;
|
|
||||||
static QIcon::Mode iconModeFromState(QStyle::State state);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QAbstractItemView* view_;
|
|
||||||
QIcon symlinkIcon_;
|
|
||||||
QSize gridSize_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_FOLDERITEMDELEGATE_H
|
|
@ -1,232 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013-2014 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
Copyright (C) 2012-2014 Andriy Grytsenko (LStranger) <andrej@rep.kiev.ua>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "foldermenu.h"
|
|
||||||
#include "createnewmenu.h"
|
|
||||||
#include "filepropsdialog.h"
|
|
||||||
#include "folderview.h"
|
|
||||||
#include "utilities.h"
|
|
||||||
#include <cstring> // for memset
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
FolderMenu::FolderMenu(FolderView* view, QWidget* parent):
|
|
||||||
QMenu(parent),
|
|
||||||
view_(view) {
|
|
||||||
|
|
||||||
ProxyFolderModel* model = view_->model();
|
|
||||||
|
|
||||||
createAction_ = new QAction(tr("Create &New"), this);
|
|
||||||
addAction(createAction_);
|
|
||||||
|
|
||||||
createAction_->setMenu(new CreateNewMenu(view_, view_->path(), this));
|
|
||||||
|
|
||||||
separator1_ = addSeparator();
|
|
||||||
|
|
||||||
pasteAction_ = new QAction(QIcon::fromTheme("edit-paste"), tr("&Paste"), this);
|
|
||||||
addAction(pasteAction_);
|
|
||||||
connect(pasteAction_, &QAction::triggered, this, &FolderMenu::onPasteActionTriggered);
|
|
||||||
|
|
||||||
separator2_ = addSeparator();
|
|
||||||
|
|
||||||
selectAllAction_ = new QAction(tr("Select &All"), this);
|
|
||||||
addAction(selectAllAction_);
|
|
||||||
connect(selectAllAction_, &QAction::triggered, this, &FolderMenu::onSelectAllActionTriggered);
|
|
||||||
|
|
||||||
invertSelectionAction_ = new QAction(tr("Invert Selection"), this);
|
|
||||||
addAction(invertSelectionAction_);
|
|
||||||
connect(invertSelectionAction_, &QAction::triggered, this, &FolderMenu::onInvertSelectionActionTriggered);
|
|
||||||
|
|
||||||
separator3_ = addSeparator();
|
|
||||||
|
|
||||||
sortAction_ = new QAction(tr("Sorting"), this);
|
|
||||||
addAction(sortAction_);
|
|
||||||
createSortMenu();
|
|
||||||
sortAction_->setMenu(sortMenu_);
|
|
||||||
|
|
||||||
showHiddenAction_ = new QAction(tr("Show Hidden"), this);
|
|
||||||
addAction(showHiddenAction_);
|
|
||||||
showHiddenAction_->setCheckable(true);
|
|
||||||
showHiddenAction_->setChecked(model->showHidden());
|
|
||||||
connect(showHiddenAction_, &QAction::triggered, this, &FolderMenu::onShowHiddenActionTriggered);
|
|
||||||
|
|
||||||
separator4_ = addSeparator();
|
|
||||||
|
|
||||||
propertiesAction_ = new QAction(tr("Folder Pr&operties"), this);
|
|
||||||
addAction(propertiesAction_);
|
|
||||||
connect(propertiesAction_, &QAction::triggered, this, &FolderMenu::onPropertiesActionTriggered);
|
|
||||||
}
|
|
||||||
|
|
||||||
FolderMenu::~FolderMenu() {
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderMenu::addSortMenuItem(QString title, int id) {
|
|
||||||
QAction* action = new QAction(title, this);
|
|
||||||
sortMenu_->addAction(action);
|
|
||||||
action->setCheckable(true);
|
|
||||||
sortActionGroup_->addAction(action);
|
|
||||||
connect(action, &QAction::triggered, this, &FolderMenu::onSortActionTriggered);
|
|
||||||
sortActions_[id] = action;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderMenu::createSortMenu() {
|
|
||||||
ProxyFolderModel* model = view_->model();
|
|
||||||
|
|
||||||
sortMenu_ = new QMenu(this);
|
|
||||||
sortActionGroup_ = new QActionGroup(sortMenu_);
|
|
||||||
sortActionGroup_->setExclusive(true);
|
|
||||||
|
|
||||||
std::memset(sortActions_, 0, sizeof(sortActions_));
|
|
||||||
|
|
||||||
addSortMenuItem(tr("By File Name"), FolderModel::ColumnFileName);
|
|
||||||
addSortMenuItem(tr("By Modification Time"), FolderModel::ColumnFileMTime);
|
|
||||||
addSortMenuItem(tr("By File Size"), FolderModel::ColumnFileSize);
|
|
||||||
addSortMenuItem(tr("By File Type"), FolderModel::ColumnFileType);
|
|
||||||
addSortMenuItem(tr("By File Owner"), FolderModel::ColumnFileOwner);
|
|
||||||
|
|
||||||
int col = model->sortColumn();
|
|
||||||
|
|
||||||
if(col >= 0 && col < FolderModel::NumOfColumns) {
|
|
||||||
sortActions_[col]->setChecked(true);;
|
|
||||||
}
|
|
||||||
|
|
||||||
sortMenu_->addSeparator();
|
|
||||||
|
|
||||||
QActionGroup* group = new QActionGroup(this);
|
|
||||||
group->setExclusive(true);
|
|
||||||
actionAscending_ = new QAction(tr("Ascending"), this);
|
|
||||||
actionAscending_->setCheckable(true);
|
|
||||||
sortMenu_->addAction(actionAscending_);
|
|
||||||
group->addAction(actionAscending_);
|
|
||||||
|
|
||||||
actionDescending_ = new QAction(tr("Descending"), this);
|
|
||||||
actionDescending_->setCheckable(true);
|
|
||||||
sortMenu_->addAction(actionDescending_);
|
|
||||||
group->addAction(actionDescending_);
|
|
||||||
|
|
||||||
if(model->sortOrder() == Qt::AscendingOrder)
|
|
||||||
actionAscending_->setChecked(true);
|
|
||||||
else
|
|
||||||
actionDescending_->setChecked(true);
|
|
||||||
|
|
||||||
connect(actionAscending_, &QAction::triggered, this, &FolderMenu::onSortOrderActionTriggered);
|
|
||||||
connect(actionDescending_, &QAction::triggered, this, &FolderMenu::onSortOrderActionTriggered);
|
|
||||||
|
|
||||||
sortMenu_->addSeparator();
|
|
||||||
|
|
||||||
QAction* actionFolderFirst = new QAction(tr("Folder First"), this);
|
|
||||||
sortMenu_->addAction(actionFolderFirst);
|
|
||||||
actionFolderFirst->setCheckable(true);
|
|
||||||
|
|
||||||
if(model->folderFirst())
|
|
||||||
actionFolderFirst->setChecked(true);
|
|
||||||
|
|
||||||
connect(actionFolderFirst, &QAction::triggered, this, &FolderMenu::onFolderFirstActionTriggered);
|
|
||||||
|
|
||||||
QAction* actionCaseSensitive = new QAction(tr("Case Sensitive"), this);
|
|
||||||
sortMenu_->addAction(actionCaseSensitive);
|
|
||||||
actionCaseSensitive->setCheckable(true);
|
|
||||||
|
|
||||||
if(model->sortCaseSensitivity() == Qt::CaseSensitive)
|
|
||||||
actionCaseSensitive->setChecked(true);
|
|
||||||
|
|
||||||
connect(actionCaseSensitive, &QAction::triggered, this, &FolderMenu::onCaseSensitiveActionTriggered);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderMenu::onPasteActionTriggered() {
|
|
||||||
FmPath* folderPath = view_->path();
|
|
||||||
|
|
||||||
if(folderPath)
|
|
||||||
pasteFilesFromClipboard(folderPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderMenu::onSelectAllActionTriggered() {
|
|
||||||
view_->selectAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderMenu::onInvertSelectionActionTriggered() {
|
|
||||||
view_->invertSelection();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderMenu::onSortActionTriggered(bool checked) {
|
|
||||||
ProxyFolderModel* model = view_->model();
|
|
||||||
|
|
||||||
if(model) {
|
|
||||||
QAction* action = static_cast<QAction*>(sender());
|
|
||||||
|
|
||||||
for(int col = 0; col < FolderModel::NumOfColumns; ++col) {
|
|
||||||
if(action == sortActions_[col]) {
|
|
||||||
model->sort(col, model->sortOrder());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderMenu::onSortOrderActionTriggered(bool checked) {
|
|
||||||
ProxyFolderModel* model = view_->model();
|
|
||||||
|
|
||||||
if(model) {
|
|
||||||
QAction* action = static_cast<QAction*>(sender());
|
|
||||||
Qt::SortOrder order;
|
|
||||||
|
|
||||||
if(action == actionAscending_)
|
|
||||||
order = Qt::AscendingOrder;
|
|
||||||
else
|
|
||||||
order = Qt::DescendingOrder;
|
|
||||||
|
|
||||||
model->sort(model->sortColumn(), order);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderMenu::onShowHiddenActionTriggered(bool checked) {
|
|
||||||
ProxyFolderModel* model = view_->model();
|
|
||||||
|
|
||||||
if(model) {
|
|
||||||
qDebug("show hidden: %d", checked);
|
|
||||||
model->setShowHidden(checked);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderMenu::onCaseSensitiveActionTriggered(bool checked) {
|
|
||||||
ProxyFolderModel* model = view_->model();
|
|
||||||
|
|
||||||
if(model) {
|
|
||||||
model->setSortCaseSensitivity(checked ? Qt::CaseSensitive : Qt::CaseInsensitive);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderMenu::onFolderFirstActionTriggered(bool checked) {
|
|
||||||
ProxyFolderModel* model = view_->model();
|
|
||||||
|
|
||||||
if(model) {
|
|
||||||
model->setFolderFirst(checked);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderMenu::onPropertiesActionTriggered() {
|
|
||||||
FmFileInfo* folderInfo = view_->folderInfo();
|
|
||||||
|
|
||||||
if(folderInfo)
|
|
||||||
FilePropsDialog::showForFile(folderInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Fm
|
|
@ -1,127 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef FM_FOLDERMENU_H
|
|
||||||
#define FM_FOLDERMENU_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QMenu>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
#include "foldermodel.h"
|
|
||||||
|
|
||||||
class QAction;
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class FolderView;
|
|
||||||
|
|
||||||
class LIBFM_QT_API FolderMenu : public QMenu {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit FolderMenu(FolderView* view, QWidget* parent = 0);
|
|
||||||
virtual ~FolderMenu();
|
|
||||||
|
|
||||||
QAction* createAction() {
|
|
||||||
return createAction_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction* separator1() {
|
|
||||||
return separator1_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction* pasteAction() {
|
|
||||||
return pasteAction_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction* separator2() {
|
|
||||||
return separator2_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction* selectAllAction() {
|
|
||||||
return selectAllAction_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction* invertSelectionAction() {
|
|
||||||
return invertSelectionAction_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction* separator3() {
|
|
||||||
return separator3_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction* sortAction() {
|
|
||||||
return sortAction_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction* showHiddenAction() {
|
|
||||||
return showHiddenAction_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction* separator4() {
|
|
||||||
return separator4_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAction* propertiesAction() {
|
|
||||||
return propertiesAction_;
|
|
||||||
}
|
|
||||||
|
|
||||||
FolderView* view() {
|
|
||||||
return view_;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Q_SLOTS:
|
|
||||||
void onPasteActionTriggered();
|
|
||||||
void onSelectAllActionTriggered();
|
|
||||||
void onInvertSelectionActionTriggered();
|
|
||||||
void onSortActionTriggered(bool checked);
|
|
||||||
void onSortOrderActionTriggered(bool checked);
|
|
||||||
void onShowHiddenActionTriggered(bool checked);
|
|
||||||
void onCaseSensitiveActionTriggered(bool checked);
|
|
||||||
void onFolderFirstActionTriggered(bool checked);
|
|
||||||
void onPropertiesActionTriggered();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void createSortMenu();
|
|
||||||
void addSortMenuItem(QString title, int id);
|
|
||||||
|
|
||||||
private:
|
|
||||||
FolderView* view_;
|
|
||||||
QAction* createAction_;
|
|
||||||
QAction* separator1_;
|
|
||||||
QAction* pasteAction_;
|
|
||||||
QAction* separator2_;
|
|
||||||
QAction* selectAllAction_;
|
|
||||||
QAction* invertSelectionAction_;
|
|
||||||
QAction* separator3_;
|
|
||||||
QAction* sortAction_;
|
|
||||||
QActionGroup* sortActionGroup_;
|
|
||||||
QMenu* sortMenu_;
|
|
||||||
QAction* sortActions_[FolderModel::NumOfColumns];
|
|
||||||
QAction* actionAscending_;
|
|
||||||
QAction* actionDescending_;
|
|
||||||
QAction* showHiddenAction_;
|
|
||||||
QAction* separator4_;
|
|
||||||
QAction* propertiesAction_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_FOLDERMENU_H
|
|
@ -1,556 +0,0 @@
|
|||||||
/*
|
|
||||||
<one line to give the library's name and an idea of what it does.>
|
|
||||||
Copyright (C) 2012 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 "foldermodel.h"
|
|
||||||
#include "icontheme.h"
|
|
||||||
#include <iostream>
|
|
||||||
#include <QtAlgorithms>
|
|
||||||
#include <QVector>
|
|
||||||
#include <qmimedata.h>
|
|
||||||
#include <QMimeData>
|
|
||||||
#include <QByteArray>
|
|
||||||
#include <QPixmap>
|
|
||||||
#include <QPainter>
|
|
||||||
#include "utilities.h"
|
|
||||||
#include "fileoperation.h"
|
|
||||||
#include "thumbnailloader.h"
|
|
||||||
|
|
||||||
using namespace Fm;
|
|
||||||
|
|
||||||
FolderModel::FolderModel() :
|
|
||||||
folder_(NULL) {
|
|
||||||
/*
|
|
||||||
ColumnIcon,
|
|
||||||
ColumnName,
|
|
||||||
ColumnFileType,
|
|
||||||
ColumnMTime,
|
|
||||||
NumOfColumns
|
|
||||||
*/
|
|
||||||
thumbnailRefCounts.reserve(4);
|
|
||||||
|
|
||||||
// reload all icons when the icon theme is changed
|
|
||||||
connect(IconTheme::instance(), &IconTheme::changed, this, &FolderModel::updateIcons);
|
|
||||||
}
|
|
||||||
|
|
||||||
FolderModel::~FolderModel() {
|
|
||||||
qDebug("delete FolderModel");
|
|
||||||
|
|
||||||
if(folder_)
|
|
||||||
setFolder(NULL);
|
|
||||||
|
|
||||||
// if the thumbnail requests list is not empty, cancel them
|
|
||||||
if(!thumbnailResults.empty()) {
|
|
||||||
Q_FOREACH(FmThumbnailLoader* res, thumbnailResults) {
|
|
||||||
ThumbnailLoader::cancel(res);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderModel::setFolder(FmFolder* new_folder) {
|
|
||||||
if(folder_) {
|
|
||||||
removeAll(); // remove old items
|
|
||||||
g_signal_handlers_disconnect_by_func(folder_, gpointer(onStartLoading), this);
|
|
||||||
g_signal_handlers_disconnect_by_func(folder_, gpointer(onFinishLoading), this);
|
|
||||||
g_signal_handlers_disconnect_by_func(folder_, gpointer(onFilesAdded), this);
|
|
||||||
g_signal_handlers_disconnect_by_func(folder_, gpointer(onFilesChanged), this);
|
|
||||||
g_signal_handlers_disconnect_by_func(folder_, gpointer(onFilesRemoved), this);
|
|
||||||
g_object_unref(folder_);
|
|
||||||
}
|
|
||||||
if(new_folder) {
|
|
||||||
folder_ = FM_FOLDER(g_object_ref(new_folder));
|
|
||||||
g_signal_connect(folder_, "start-loading", G_CALLBACK(onStartLoading), this);
|
|
||||||
g_signal_connect(folder_, "finish-loading", G_CALLBACK(onFinishLoading), this);
|
|
||||||
g_signal_connect(folder_, "files-added", G_CALLBACK(onFilesAdded), this);
|
|
||||||
g_signal_connect(folder_, "files-changed", G_CALLBACK(onFilesChanged), this);
|
|
||||||
g_signal_connect(folder_, "files-removed", G_CALLBACK(onFilesRemoved), this);
|
|
||||||
// handle the case if the folder is already loaded
|
|
||||||
if(fm_folder_is_loaded(folder_))
|
|
||||||
insertFiles(0, fm_folder_get_files(folder_));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
folder_ = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderModel::onStartLoading(FmFolder* folder, gpointer user_data) {
|
|
||||||
FolderModel* model = static_cast<FolderModel*>(user_data);
|
|
||||||
// remove all items
|
|
||||||
model->removeAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderModel::onFinishLoading(FmFolder* folder, gpointer user_data) {
|
|
||||||
Q_UNUSED(folder)
|
|
||||||
Q_UNUSED(user_data)
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderModel::onFilesAdded(FmFolder* folder, GSList* files, gpointer user_data) {
|
|
||||||
FolderModel* model = static_cast<FolderModel*>(user_data);
|
|
||||||
int n_files = g_slist_length(files);
|
|
||||||
model->beginInsertRows(QModelIndex(), model->items.count(), model->items.count() + n_files - 1);
|
|
||||||
for(GSList* l = files; l; l = l->next) {
|
|
||||||
FmFileInfo* info = FM_FILE_INFO(l->data);
|
|
||||||
FolderModelItem item(info);
|
|
||||||
/*
|
|
||||||
if(fm_file_info_is_hidden(info)) {
|
|
||||||
model->hiddenItems.append(item);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
model->items.append(item);
|
|
||||||
}
|
|
||||||
model->endInsertRows();
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
void FolderModel::onFilesChanged(FmFolder* folder, GSList* files, gpointer user_data) {
|
|
||||||
FolderModel* model = static_cast<FolderModel*>(user_data);
|
|
||||||
for(GSList* l = files; l; l = l->next) {
|
|
||||||
FmFileInfo* info = FM_FILE_INFO(l->data);
|
|
||||||
int row;
|
|
||||||
QList<FolderModelItem>::iterator it = model->findItemByFileInfo(info, &row);
|
|
||||||
if(it != model->items.end()) {
|
|
||||||
FolderModelItem& item = *it;
|
|
||||||
// try to update the item
|
|
||||||
item.displayName = QString::fromUtf8(fm_file_info_get_disp_name(info));
|
|
||||||
item.updateIcon();
|
|
||||||
item.thumbnails.clear();
|
|
||||||
QModelIndex index = model->createIndex(row, 0, &item);
|
|
||||||
Q_EMIT model->dataChanged(index, index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
void FolderModel::onFilesRemoved(FmFolder* folder, GSList* files, gpointer user_data) {
|
|
||||||
FolderModel* model = static_cast<FolderModel*>(user_data);
|
|
||||||
for(GSList* l = files; l; l = l->next) {
|
|
||||||
FmFileInfo* info = FM_FILE_INFO(l->data);
|
|
||||||
const char* name = fm_file_info_get_name(info);
|
|
||||||
int row;
|
|
||||||
QList<FolderModelItem>::iterator it = model->findItemByName(name, &row);
|
|
||||||
if(it != model->items.end()) {
|
|
||||||
model->beginRemoveRows(QModelIndex(), row, row);
|
|
||||||
model->items.erase(it);
|
|
||||||
model->endRemoveRows();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderModel::insertFiles(int row, FmFileInfoList* files) {
|
|
||||||
int n_files = fm_file_info_list_get_length(files);
|
|
||||||
beginInsertRows(QModelIndex(), row, row + n_files - 1);
|
|
||||||
for(GList* l = fm_file_info_list_peek_head_link(files); l; l = l->next) {
|
|
||||||
FolderModelItem item(FM_FILE_INFO(l->data));
|
|
||||||
items.append(item);
|
|
||||||
}
|
|
||||||
endInsertRows();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderModel::removeAll() {
|
|
||||||
if(items.empty())
|
|
||||||
return;
|
|
||||||
beginRemoveRows(QModelIndex(), 0, items.size() - 1);
|
|
||||||
items.clear();
|
|
||||||
endRemoveRows();
|
|
||||||
}
|
|
||||||
|
|
||||||
int FolderModel::rowCount(const QModelIndex & parent) const {
|
|
||||||
if(parent.isValid())
|
|
||||||
return 0;
|
|
||||||
return items.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
int FolderModel::columnCount (const QModelIndex & parent = QModelIndex()) const {
|
|
||||||
if(parent.isValid())
|
|
||||||
return 0;
|
|
||||||
return NumOfColumns;
|
|
||||||
}
|
|
||||||
|
|
||||||
FolderModelItem* FolderModel::itemFromIndex(const QModelIndex& index) const {
|
|
||||||
return reinterpret_cast<FolderModelItem*>(index.internalPointer());
|
|
||||||
}
|
|
||||||
|
|
||||||
FmFileInfo* FolderModel::fileInfoFromIndex(const QModelIndex& index) const {
|
|
||||||
FolderModelItem* item = itemFromIndex(index);
|
|
||||||
return item ? item->info : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant FolderModel::data(const QModelIndex & index, int role = Qt::DisplayRole) const {
|
|
||||||
if(!index.isValid() || index.row() > items.size() || index.column() >= NumOfColumns) {
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
FolderModelItem* item = itemFromIndex(index);
|
|
||||||
FmFileInfo* info = item->info;
|
|
||||||
|
|
||||||
switch(role) {
|
|
||||||
case Qt::ToolTipRole:
|
|
||||||
return QVariant(item->displayName);
|
|
||||||
case Qt::DisplayRole: {
|
|
||||||
switch(index.column()) {
|
|
||||||
case ColumnFileName: {
|
|
||||||
return QVariant(item->displayName);
|
|
||||||
}
|
|
||||||
case ColumnFileType: {
|
|
||||||
FmMimeType* mime = fm_file_info_get_mime_type(info);
|
|
||||||
const char* desc = fm_mime_type_get_desc(mime);
|
|
||||||
return QString::fromUtf8(desc);
|
|
||||||
}
|
|
||||||
case ColumnFileMTime: {
|
|
||||||
const char* name = fm_file_info_get_disp_mtime(info);
|
|
||||||
return QString::fromUtf8(name);
|
|
||||||
}
|
|
||||||
case ColumnFileSize: {
|
|
||||||
const char* name = fm_file_info_get_disp_size(info);
|
|
||||||
return QString::fromUtf8(name);
|
|
||||||
}
|
|
||||||
case ColumnFileOwner: {
|
|
||||||
const char* name = fm_file_info_get_disp_owner(info);
|
|
||||||
return QString::fromUtf8(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case Qt::DecorationRole: {
|
|
||||||
if(index.column() == 0) {
|
|
||||||
// QPixmap pix = IconTheme::loadIcon(fm_file_info_get_icon(info), iconSize_);
|
|
||||||
return QVariant(item->icon);
|
|
||||||
// return QVariant(pix);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case FileInfoRole:
|
|
||||||
return qVariantFromValue((void*)info);
|
|
||||||
}
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant FolderModel::headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const {
|
|
||||||
if(role == Qt::DisplayRole) {
|
|
||||||
if(orientation == Qt::Horizontal) {
|
|
||||||
QString title;
|
|
||||||
switch(section) {
|
|
||||||
case ColumnFileName:
|
|
||||||
title = tr("Name");
|
|
||||||
break;
|
|
||||||
case ColumnFileType:
|
|
||||||
title = tr("Type");
|
|
||||||
break;
|
|
||||||
case ColumnFileSize:
|
|
||||||
title = tr("Size");
|
|
||||||
break;
|
|
||||||
case ColumnFileMTime:
|
|
||||||
title = tr("Modified");
|
|
||||||
break;
|
|
||||||
case ColumnFileOwner:
|
|
||||||
title = tr("Owner");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return QVariant(title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return QVariant();
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex FolderModel::index(int row, int column, const QModelIndex & parent) const {
|
|
||||||
if(row <0 || row >= items.size() || column < 0 || column >= NumOfColumns)
|
|
||||||
return QModelIndex();
|
|
||||||
const FolderModelItem& item = items.at(row);
|
|
||||||
return createIndex(row, column, (void*)&item);
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex FolderModel::parent(const QModelIndex & index) const {
|
|
||||||
return QModelIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt::ItemFlags FolderModel::flags(const QModelIndex& index) const {
|
|
||||||
// FIXME: should not return same flags unconditionally for all columns
|
|
||||||
Qt::ItemFlags flags;
|
|
||||||
if(index.isValid()) {
|
|
||||||
flags = Qt::ItemIsEnabled|Qt::ItemIsSelectable;
|
|
||||||
if(index.column() == ColumnFileName)
|
|
||||||
flags |= (Qt::ItemIsDragEnabled|Qt::ItemIsDropEnabled);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
flags = Qt::ItemIsDropEnabled;
|
|
||||||
}
|
|
||||||
return flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: this is very inefficient and should be replaced with a
|
|
||||||
// more reasonable implementation later.
|
|
||||||
QList<FolderModelItem>::iterator FolderModel::findItemByPath(FmPath* path, int* row) {
|
|
||||||
QList<FolderModelItem>::iterator it = items.begin();
|
|
||||||
int i = 0;
|
|
||||||
while(it != items.end()) {
|
|
||||||
FolderModelItem& item = *it;
|
|
||||||
FmPath* item_path = fm_file_info_get_path(item.info);
|
|
||||||
if(fm_path_equal(item_path, path)) {
|
|
||||||
*row = i;
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
++it;
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
return items.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: this is very inefficient and should be replaced with a
|
|
||||||
// more reasonable implementation later.
|
|
||||||
QList<FolderModelItem>::iterator FolderModel::findItemByName(const char* name, int* row) {
|
|
||||||
QList<FolderModelItem>::iterator it = items.begin();
|
|
||||||
int i = 0;
|
|
||||||
while(it != items.end()) {
|
|
||||||
FolderModelItem& item = *it;
|
|
||||||
const char* item_name = fm_file_info_get_name(item.info);
|
|
||||||
if(strcmp(name, item_name) == 0) {
|
|
||||||
*row = i;
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
++it;
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
return items.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
QList< FolderModelItem >::iterator FolderModel::findItemByFileInfo(FmFileInfo* info, int* row) {
|
|
||||||
QList<FolderModelItem>::iterator it = items.begin();
|
|
||||||
int i = 0;
|
|
||||||
while(it != items.end()) {
|
|
||||||
FolderModelItem& item = *it;
|
|
||||||
if(item.info == info) {
|
|
||||||
*row = i;
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
++it;
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
return items.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList FolderModel::mimeTypes() const {
|
|
||||||
qDebug("FolderModel::mimeTypes");
|
|
||||||
QStringList types = QAbstractItemModel::mimeTypes();
|
|
||||||
// now types contains "application/x-qabstractitemmodeldatalist"
|
|
||||||
types << "text/uri-list";
|
|
||||||
// types << "x-special/gnome-copied-files";
|
|
||||||
return types;
|
|
||||||
}
|
|
||||||
|
|
||||||
QMimeData* FolderModel::mimeData(const QModelIndexList& indexes) const {
|
|
||||||
QMimeData* data = QAbstractItemModel::mimeData(indexes);
|
|
||||||
qDebug("FolderModel::mimeData");
|
|
||||||
// build a uri list
|
|
||||||
QByteArray urilist;
|
|
||||||
urilist.reserve(4096);
|
|
||||||
|
|
||||||
QModelIndexList::const_iterator it;
|
|
||||||
for(it = indexes.constBegin(); it != indexes.end(); ++it) {
|
|
||||||
const QModelIndex index = *it;
|
|
||||||
FolderModelItem* item = itemFromIndex(index);
|
|
||||||
if(item) {
|
|
||||||
FmPath* path = fm_file_info_get_path(item->info);
|
|
||||||
char* uri = fm_path_to_uri(path);
|
|
||||||
urilist.append(uri);
|
|
||||||
urilist.append('\n');
|
|
||||||
g_free(uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
data->setData("text/uri-list", urilist);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FolderModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) {
|
|
||||||
qDebug("FolderModel::dropMimeData");
|
|
||||||
if(!folder_)
|
|
||||||
return false;
|
|
||||||
FmPath* destPath;
|
|
||||||
if(parent.isValid()) { // drop on an item
|
|
||||||
FmFileInfo* info;
|
|
||||||
if(row == -1 && column == -1)
|
|
||||||
info = fileInfoFromIndex(parent);
|
|
||||||
else {
|
|
||||||
QModelIndex itemIndex = parent.child(row, column);
|
|
||||||
info = fileInfoFromIndex(itemIndex);
|
|
||||||
}
|
|
||||||
if(info)
|
|
||||||
destPath = fm_file_info_get_path(info);
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else { // drop on blank area of the folder
|
|
||||||
destPath = path();
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: should we put this in dropEvent handler of FolderView instead?
|
|
||||||
if(data->hasUrls()) {
|
|
||||||
qDebug("drop action: %d", action);
|
|
||||||
FmPathList* srcPaths = pathListFromQUrls(data->urls());
|
|
||||||
switch(action) {
|
|
||||||
case Qt::CopyAction:
|
|
||||||
FileOperation::copyFiles(srcPaths, destPath);
|
|
||||||
break;
|
|
||||||
case Qt::MoveAction:
|
|
||||||
FileOperation::moveFiles(srcPaths, destPath);
|
|
||||||
break;
|
|
||||||
case Qt::LinkAction:
|
|
||||||
FileOperation::symlinkFiles(srcPaths, destPath);
|
|
||||||
default:
|
|
||||||
fm_path_list_unref(srcPaths);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
fm_path_list_unref(srcPaths);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if(data->hasFormat("application/x-qabstractitemmodeldatalist")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return QAbstractListModel::dropMimeData(data, action, row, column, parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt::DropActions FolderModel::supportedDropActions() const {
|
|
||||||
qDebug("FolderModel::supportedDropActions");
|
|
||||||
return Qt::CopyAction|Qt::MoveAction|Qt::LinkAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ask the model to load thumbnails of the specified size
|
|
||||||
void FolderModel::cacheThumbnails(int size) {
|
|
||||||
QVector<QPair<int, int> >::iterator it;
|
|
||||||
for(it = thumbnailRefCounts.begin(); it != thumbnailRefCounts.end(); ++it) {
|
|
||||||
if(it->first == size) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(it != thumbnailRefCounts.end())
|
|
||||||
++it->second;
|
|
||||||
else
|
|
||||||
thumbnailRefCounts.append(QPair<int, int>(size, 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ask the model to free cached thumbnails of the specified size
|
|
||||||
void FolderModel::releaseThumbnails(int size) {
|
|
||||||
QVector<QPair<int, int> >::iterator it;
|
|
||||||
for(it = thumbnailRefCounts.begin(); it != thumbnailRefCounts.end(); ++it) {
|
|
||||||
if(it->first == size) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(it != thumbnailRefCounts.end()) {
|
|
||||||
--it->second;
|
|
||||||
if(it->second == 0) {
|
|
||||||
thumbnailRefCounts.erase(it);
|
|
||||||
|
|
||||||
// remove thumbnails that ara queued for loading from thumbnailResults
|
|
||||||
QLinkedList<FmThumbnailLoader*>::iterator it;
|
|
||||||
for(it = thumbnailResults.begin(); it != thumbnailResults.end();) {
|
|
||||||
QLinkedList<FmThumbnailLoader*>::iterator next = it + 1;
|
|
||||||
FmThumbnailLoader* res = *it;
|
|
||||||
if(ThumbnailLoader::size(res) == size) {
|
|
||||||
ThumbnailLoader::cancel(res);
|
|
||||||
thumbnailResults.erase(it);
|
|
||||||
}
|
|
||||||
it = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove all cached thumbnails of the specified size
|
|
||||||
QList<FolderModelItem>::iterator itemIt;
|
|
||||||
for(itemIt = items.begin(); itemIt != items.end(); ++itemIt) {
|
|
||||||
FolderModelItem& item = *itemIt;
|
|
||||||
item.removeThumbnail(size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderModel::onThumbnailLoaded(FmThumbnailLoader* res, gpointer user_data) {
|
|
||||||
FolderModel* pThis = reinterpret_cast<FolderModel*>(user_data);
|
|
||||||
QLinkedList<FmThumbnailLoader*>::iterator it;
|
|
||||||
for(it = pThis->thumbnailResults.begin(); it != pThis->thumbnailResults.end(); ++it) {
|
|
||||||
if(*it == res) { // the thumbnail result is in our list
|
|
||||||
pThis->thumbnailResults.erase(it); // remove it from the list
|
|
||||||
FmFileInfo* info = ThumbnailLoader::fileInfo(res);
|
|
||||||
int row = -1;
|
|
||||||
// find the model item this thumbnail belongs to
|
|
||||||
QList<FolderModelItem>::iterator it = pThis->findItemByFileInfo(info, &row);
|
|
||||||
if(it != pThis->items.end()) {
|
|
||||||
// the file is found in our model
|
|
||||||
FolderModelItem& item = *it;
|
|
||||||
QModelIndex index = pThis->createIndex(row, 0, (void*)&item);
|
|
||||||
// store the image in the folder model item.
|
|
||||||
int size = ThumbnailLoader::size(res);
|
|
||||||
QImage image = ThumbnailLoader::image(res);
|
|
||||||
FolderModelItem::Thumbnail* thumbnail = item.findThumbnail(size);
|
|
||||||
thumbnail->image = image;
|
|
||||||
// qDebug("thumbnail loaded for: %s, size: %d", item.displayName.toUtf8().constData(), size);
|
|
||||||
if(image.isNull())
|
|
||||||
thumbnail->status = FolderModelItem::ThumbnailFailed;
|
|
||||||
else {
|
|
||||||
thumbnail->status = FolderModelItem::ThumbnailLoaded;
|
|
||||||
// FIXME: due to bugs in Qt's QStyledItemDelegate, if the image width and height
|
|
||||||
// are not the same, painting errors will happen. It's quite unfortunate.
|
|
||||||
// Let's do some padding to make its width and height equals.
|
|
||||||
// This greatly decrease performance :-(
|
|
||||||
// Later if we can re-implement our own item delegate, this can be avoided.
|
|
||||||
QPixmap pixmap = QPixmap(size, size);
|
|
||||||
pixmap.fill(QColor(0, 0, 0, 0)); // fill the pixmap with transparent color (alpha:0)
|
|
||||||
QPainter painter(&pixmap);
|
|
||||||
int x = (size - image.width()) / 2;
|
|
||||||
int y = (size - image.height()) / 2;
|
|
||||||
painter.drawImage(QPoint(x, y), image); // draw the image to the pixmap at center.
|
|
||||||
// FIXME: should we cache QPixmap instead for performance reason?
|
|
||||||
thumbnail->image = pixmap.toImage(); // convert it back to image
|
|
||||||
|
|
||||||
// tell the world that we have the thumbnail loaded
|
|
||||||
Q_EMIT pThis->thumbnailLoaded(index, size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// get a thumbnail of size at the index
|
|
||||||
// if a thumbnail is not yet loaded, this will initiate loading of the thumbnail.
|
|
||||||
QImage FolderModel::thumbnailFromIndex(const QModelIndex& index, int size) {
|
|
||||||
FolderModelItem* item = itemFromIndex(index);
|
|
||||||
if(item) {
|
|
||||||
FolderModelItem::Thumbnail* thumbnail = item->findThumbnail(size);
|
|
||||||
// qDebug("FolderModel::thumbnailFromIndex: %d, %s", thumbnail->status, item->displayName.toUtf8().data());
|
|
||||||
switch(thumbnail->status) {
|
|
||||||
case FolderModelItem::ThumbnailNotChecked: {
|
|
||||||
// load the thumbnail
|
|
||||||
FmThumbnailLoader* res = ThumbnailLoader::load(item->info, size, onThumbnailLoaded, this);
|
|
||||||
thumbnailResults.push_back(res);
|
|
||||||
thumbnail->status = FolderModelItem::ThumbnailLoading;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case FolderModelItem::ThumbnailLoaded:
|
|
||||||
return thumbnail->image;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return QImage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderModel::updateIcons() {
|
|
||||||
QList<FolderModelItem>::iterator it = items.begin();
|
|
||||||
for(;it != items.end(); ++it) {
|
|
||||||
(*it).updateIcon();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,121 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2012 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_FOLDERMODEL_H
|
|
||||||
#define FM_FOLDERMODEL_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QAbstractListModel>
|
|
||||||
#include <QIcon>
|
|
||||||
#include <QImage>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
#include <QList>
|
|
||||||
#include <QVector>
|
|
||||||
#include <QLinkedList>
|
|
||||||
#include <QPair>
|
|
||||||
#include "foldermodelitem.h"
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class LIBFM_QT_API FolderModel : public QAbstractListModel {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
|
|
||||||
enum Role {
|
|
||||||
FileInfoRole = Qt::UserRole
|
|
||||||
};
|
|
||||||
|
|
||||||
enum ColumnId {
|
|
||||||
ColumnFileName,
|
|
||||||
ColumnFileType,
|
|
||||||
ColumnFileSize,
|
|
||||||
ColumnFileMTime,
|
|
||||||
ColumnFileOwner,
|
|
||||||
NumOfColumns
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
FolderModel();
|
|
||||||
virtual ~FolderModel();
|
|
||||||
|
|
||||||
FmFolder* folder() {
|
|
||||||
return folder_;
|
|
||||||
}
|
|
||||||
void setFolder(FmFolder* new_folder);
|
|
||||||
|
|
||||||
FmPath* path() {
|
|
||||||
return folder_ ? fm_folder_get_path(folder_) : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int rowCount(const QModelIndex & parent = QModelIndex()) const;
|
|
||||||
int columnCount (const QModelIndex & parent) const;
|
|
||||||
QVariant data(const QModelIndex & index, int role) const;
|
|
||||||
QVariant headerData(int section, Qt::Orientation orientation, int role) const;
|
|
||||||
QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const;
|
|
||||||
QModelIndex parent( const QModelIndex & index ) const;
|
|
||||||
// void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
|
|
||||||
|
|
||||||
Qt::ItemFlags flags(const QModelIndex & index) const;
|
|
||||||
|
|
||||||
virtual QStringList mimeTypes() const;
|
|
||||||
virtual QMimeData* mimeData(const QModelIndexList & indexes) const;
|
|
||||||
virtual Qt::DropActions supportedDropActions() const;
|
|
||||||
virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent);
|
|
||||||
|
|
||||||
FmFileInfo* fileInfoFromIndex(const QModelIndex& index) const;
|
|
||||||
FolderModelItem* itemFromIndex(const QModelIndex& index) const;
|
|
||||||
QImage thumbnailFromIndex(const QModelIndex& index, int size);
|
|
||||||
|
|
||||||
void cacheThumbnails(int size);
|
|
||||||
void releaseThumbnails(int size);
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void thumbnailLoaded(const QModelIndex& index, int size);
|
|
||||||
|
|
||||||
public Q_SLOTS:
|
|
||||||
void updateIcons();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
static void onStartLoading(FmFolder* folder, gpointer user_data);
|
|
||||||
static void onFinishLoading(FmFolder* folder, gpointer user_data);
|
|
||||||
static void onFilesAdded(FmFolder* folder, GSList* files, gpointer user_data);
|
|
||||||
static void onFilesChanged(FmFolder* folder, GSList* files, gpointer user_data);
|
|
||||||
static void onFilesRemoved(FmFolder* folder, GSList* files, gpointer user_data);
|
|
||||||
static void onThumbnailLoaded(FmThumbnailLoader *res, gpointer user_data);
|
|
||||||
|
|
||||||
void insertFiles(int row, FmFileInfoList* files);
|
|
||||||
void removeAll();
|
|
||||||
QList<FolderModelItem>::iterator findItemByPath(FmPath* path, int* row);
|
|
||||||
QList<FolderModelItem>::iterator findItemByName(const char* name, int* row);
|
|
||||||
QList<FolderModelItem>::iterator findItemByFileInfo(FmFileInfo* info, int* row);
|
|
||||||
|
|
||||||
private:
|
|
||||||
FmFolder* folder_;
|
|
||||||
// FIXME: should we use a hash table here so item lookup becomes much faster?
|
|
||||||
QList<FolderModelItem> items;
|
|
||||||
|
|
||||||
// record what size of thumbnails we should cache in an array of <size, refCount> pairs.
|
|
||||||
QVector<QPair<int, int> > thumbnailRefCounts;
|
|
||||||
QLinkedList<FmThumbnailLoader*> thumbnailResults;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_FOLDERMODEL_H
|
|
@ -1,93 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* Copyright (C) 2012 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 "foldermodelitem.h"
|
|
||||||
|
|
||||||
using namespace Fm;
|
|
||||||
|
|
||||||
FolderModelItem::FolderModelItem(FmFileInfo* _info):
|
|
||||||
info(fm_file_info_ref(_info)) {
|
|
||||||
displayName = QString::fromUtf8(fm_file_info_get_disp_name(info));
|
|
||||||
icon = IconTheme::icon(fm_file_info_get_icon(_info));
|
|
||||||
thumbnails.reserve(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
FolderModelItem::FolderModelItem(const FolderModelItem& other) {
|
|
||||||
info = other.info ? fm_file_info_ref(other.info) : NULL;
|
|
||||||
displayName = QString::fromUtf8(fm_file_info_get_disp_name(info));
|
|
||||||
icon = other.icon;
|
|
||||||
thumbnails = other.thumbnails;
|
|
||||||
}
|
|
||||||
|
|
||||||
FolderModelItem::~FolderModelItem() {
|
|
||||||
if(info)
|
|
||||||
fm_file_info_unref(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
// find thumbnail of the specified size
|
|
||||||
// The returned thumbnail item is temporary and short-lived
|
|
||||||
// If you need to use the struct later, copy it to your own struct to keep it.
|
|
||||||
FolderModelItem::Thumbnail* FolderModelItem::findThumbnail(int size) {
|
|
||||||
QVector<Thumbnail>::iterator it;
|
|
||||||
for(it = thumbnails.begin(); it != thumbnails.end(); ++it) {
|
|
||||||
if(it->size == size) { // an image of the same size is found
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(it == thumbnails.end()) {
|
|
||||||
Thumbnail thumbnail;
|
|
||||||
thumbnail.status = ThumbnailNotChecked;
|
|
||||||
thumbnail.size = size;
|
|
||||||
thumbnails.append(thumbnail);
|
|
||||||
}
|
|
||||||
return &thumbnails.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove cached thumbnail of the specified size
|
|
||||||
void FolderModelItem::removeThumbnail(int size) {
|
|
||||||
QVector<Thumbnail>::iterator it;
|
|
||||||
for(it = thumbnails.begin(); it != thumbnails.end(); ++it) {
|
|
||||||
if(it->size == size) { // an image of the same size is found
|
|
||||||
thumbnails.erase(it);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
// cache the thumbnail of the specified size in the folder item
|
|
||||||
void FolderModelItem::setThumbnail(int size, QImage image) {
|
|
||||||
QVector<Thumbnail>::iterator it;
|
|
||||||
for(it = thumbnails.begin(); it != thumbnails.end(); ++it) {
|
|
||||||
if(it->size == size) { // an image of the same size already exists
|
|
||||||
it->image = image; // replace it
|
|
||||||
it->status = ThumbnailLoaded;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(it == thumbnails.end()) { // the image is not found
|
|
||||||
Thumbnail thumbnail;
|
|
||||||
thumbnail.size = size;
|
|
||||||
thumbnail.status = ThumbnailLoaded;
|
|
||||||
thumbnail.image = image;
|
|
||||||
thumbnails.append(thumbnail); // add a new entry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* Copyright (C) 2012 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_FOLDERMODELITEM_H
|
|
||||||
#define FM_FOLDERMODELITEM_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
#include <QImage>
|
|
||||||
#include <QString>
|
|
||||||
#include <QIcon>
|
|
||||||
#include <QVector>
|
|
||||||
#include "icontheme.h"
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class LIBFM_QT_API FolderModelItem {
|
|
||||||
public:
|
|
||||||
|
|
||||||
enum ThumbnailStatus {
|
|
||||||
ThumbnailNotChecked,
|
|
||||||
ThumbnailLoading,
|
|
||||||
ThumbnailLoaded,
|
|
||||||
ThumbnailFailed
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Thumbnail {
|
|
||||||
int size;
|
|
||||||
ThumbnailStatus status;
|
|
||||||
QImage image;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
FolderModelItem(FmFileInfo* _info);
|
|
||||||
FolderModelItem(const FolderModelItem& other);
|
|
||||||
virtual ~FolderModelItem();
|
|
||||||
|
|
||||||
Thumbnail* findThumbnail(int size);
|
|
||||||
// void setThumbnail(int size, QImage image);
|
|
||||||
void removeThumbnail(int size);
|
|
||||||
|
|
||||||
void updateIcon() {
|
|
||||||
icon = IconTheme::icon(fm_file_info_get_icon(info));
|
|
||||||
}
|
|
||||||
|
|
||||||
QString displayName;
|
|
||||||
QIcon icon;
|
|
||||||
FmFileInfo* info;
|
|
||||||
QVector<Thumbnail> thumbnails;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_FOLDERMODELITEM_H
|
|
@ -1,970 +0,0 @@
|
|||||||
/*
|
|
||||||
<one line to give the library's name and an idea of what it does.>
|
|
||||||
Copyright (C) 2012 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 "folderview.h"
|
|
||||||
#include "foldermodel.h"
|
|
||||||
#include <QHeaderView>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
#include <QContextMenuEvent>
|
|
||||||
#include "proxyfoldermodel.h"
|
|
||||||
#include "folderitemdelegate.h"
|
|
||||||
#include "dndactionmenu.h"
|
|
||||||
#include "filemenu.h"
|
|
||||||
#include "foldermenu.h"
|
|
||||||
#include "filelauncher.h"
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QDate>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QMimeData>
|
|
||||||
#include <QHoverEvent>
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QScrollBar>
|
|
||||||
#include <QMetaType>
|
|
||||||
#include "folderview_p.h"
|
|
||||||
|
|
||||||
Q_DECLARE_OPAQUE_POINTER(FmFileInfo*)
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
FolderViewListView::FolderViewListView(QWidget* parent):
|
|
||||||
QListView(parent),
|
|
||||||
activationAllowed_(true) {
|
|
||||||
connect(this, &QListView::activated, this, &FolderViewListView::activation);
|
|
||||||
}
|
|
||||||
|
|
||||||
FolderViewListView::~FolderViewListView() {
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewListView::startDrag(Qt::DropActions supportedActions) {
|
|
||||||
if(movement() != Static)
|
|
||||||
QListView::startDrag(supportedActions);
|
|
||||||
else
|
|
||||||
QAbstractItemView::startDrag(supportedActions);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewListView::mousePressEvent(QMouseEvent* event) {
|
|
||||||
QListView::mousePressEvent(event);
|
|
||||||
static_cast<FolderView*>(parent())->childMousePressEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndex FolderViewListView::indexAt(const QPoint& point) const {
|
|
||||||
QModelIndex index = QListView::indexAt(point);
|
|
||||||
// NOTE: QListView has a severe design flaw here. It does hit-testing based on the
|
|
||||||
// total bound rect of the item. The width of an item is determined by max(icon_width, text_width).
|
|
||||||
// So if the text label is much wider than the icon, when you click outside the icon but
|
|
||||||
// the point is still within the outer bound rect, the item is still selected.
|
|
||||||
// This results in very poor usability. Let's do precise hit-testing here.
|
|
||||||
// An item is hit only when the point is in the icon or text label.
|
|
||||||
// If the point is in the bound rectangle but outside the icon or text, it should not be selected.
|
|
||||||
if(viewMode() == QListView::IconMode && index.isValid()) {
|
|
||||||
// FIXME: this hack only improves the usability partially. We still need more precise sizeHint handling.
|
|
||||||
// FolderItemDelegate* delegate = static_cast<FolderItemDelegate*>(itemDelegateForColumn(FolderModel::ColumnFileName));
|
|
||||||
// Q_ASSERT(delegate != NULL);
|
|
||||||
// We use the grid size - (2, 2) as the size of the bounding rectangle of the whole item.
|
|
||||||
// The width of the text label hence is gridSize.width - 2, and the width and height of the icon is from iconSize().
|
|
||||||
QRect visRect = visualRect(index); // visibal area on the screen
|
|
||||||
QSize itemSize = gridSize();
|
|
||||||
itemSize.setWidth(itemSize.width() - 2);
|
|
||||||
itemSize.setHeight(itemSize.height() - 2);
|
|
||||||
QSize _iconSize = iconSize();
|
|
||||||
int textHeight = itemSize.height() - _iconSize.height();
|
|
||||||
if(point.y() < visRect.bottom() - textHeight) {
|
|
||||||
// the point is in the icon area, not over the text label
|
|
||||||
int iconXMargin = (itemSize.width() - _iconSize.width()) / 2;
|
|
||||||
if(point.x() < (visRect.left() + iconXMargin) || point.x() > (visRect.right() - iconXMargin))
|
|
||||||
return QModelIndex();
|
|
||||||
}
|
|
||||||
// qDebug() << "visualRect: " << visRect << "point:" << point;
|
|
||||||
}
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// NOTE:
|
|
||||||
// QListView has a problem which I consider a bug or a design flaw.
|
|
||||||
// When you set movement property to Static, theoratically the icons
|
|
||||||
// should not be movable. However, if you turned on icon mode,
|
|
||||||
// the icons becomes freely movable despite the value of movement is Static.
|
|
||||||
// To overcome this bug, we override all drag handling methods, and
|
|
||||||
// call QAbstractItemView directly, bypassing QListView.
|
|
||||||
// In this way, we can workaround the buggy behavior.
|
|
||||||
// The drag handlers of QListView basically does the same things
|
|
||||||
// as its parent QAbstractItemView, but it also stores the currently
|
|
||||||
// dragged item and paint them in the view as needed.
|
|
||||||
// TODO: I really should file a bug report to Qt developers.
|
|
||||||
|
|
||||||
void FolderViewListView::dragEnterEvent(QDragEnterEvent* event) {
|
|
||||||
if(movement() != Static)
|
|
||||||
QListView::dragEnterEvent(event);
|
|
||||||
else
|
|
||||||
QAbstractItemView::dragEnterEvent(event);
|
|
||||||
qDebug("dragEnterEvent");
|
|
||||||
//static_cast<FolderView*>(parent())->childDragEnterEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewListView::dragLeaveEvent(QDragLeaveEvent* e) {
|
|
||||||
if(movement() != Static)
|
|
||||||
QListView::dragLeaveEvent(e);
|
|
||||||
else
|
|
||||||
QAbstractItemView::dragLeaveEvent(e);
|
|
||||||
static_cast<FolderView*>(parent())->childDragLeaveEvent(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewListView::dragMoveEvent(QDragMoveEvent* e) {
|
|
||||||
if(movement() != Static)
|
|
||||||
QListView::dragMoveEvent(e);
|
|
||||||
else
|
|
||||||
QAbstractItemView::dragMoveEvent(e);
|
|
||||||
static_cast<FolderView*>(parent())->childDragMoveEvent(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewListView::dropEvent(QDropEvent* e) {
|
|
||||||
|
|
||||||
static_cast<FolderView*>(parent())->childDropEvent(e);
|
|
||||||
|
|
||||||
if(movement() != Static)
|
|
||||||
QListView::dropEvent(e);
|
|
||||||
else
|
|
||||||
QAbstractItemView::dropEvent(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewListView::mouseReleaseEvent(QMouseEvent* event) {
|
|
||||||
bool activationWasAllowed = activationAllowed_;
|
|
||||||
if ((!style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, NULL, this)) || (event->button() != Qt::LeftButton)) {
|
|
||||||
activationAllowed_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QListView::mouseReleaseEvent(event);
|
|
||||||
|
|
||||||
activationAllowed_ = activationWasAllowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewListView::mouseDoubleClickEvent(QMouseEvent* event) {
|
|
||||||
bool activationWasAllowed = activationAllowed_;
|
|
||||||
if ((style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, NULL, this)) || (event->button() != Qt::LeftButton)) {
|
|
||||||
activationAllowed_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QListView::mouseDoubleClickEvent(event);
|
|
||||||
|
|
||||||
activationAllowed_ = activationWasAllowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewListView::activation(const QModelIndex &index) {
|
|
||||||
if (activationAllowed_) {
|
|
||||||
Q_EMIT activatedFiltered(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
FolderViewTreeView::FolderViewTreeView(QWidget* parent):
|
|
||||||
QTreeView(parent),
|
|
||||||
layoutTimer_(NULL),
|
|
||||||
doingLayout_(false),
|
|
||||||
activationAllowed_(true) {
|
|
||||||
|
|
||||||
header()->setStretchLastSection(true);
|
|
||||||
setIndentation(0);
|
|
||||||
|
|
||||||
connect(this, &QTreeView::activated, this, &FolderViewTreeView::activation);
|
|
||||||
}
|
|
||||||
|
|
||||||
FolderViewTreeView::~FolderViewTreeView() {
|
|
||||||
if(layoutTimer_)
|
|
||||||
delete layoutTimer_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewTreeView::setModel(QAbstractItemModel* model) {
|
|
||||||
QTreeView::setModel(model);
|
|
||||||
layoutColumns();
|
|
||||||
if(ProxyFolderModel* proxyModel = qobject_cast<ProxyFolderModel*>(model)) {
|
|
||||||
connect(proxyModel, &ProxyFolderModel::sortFilterChanged, this, &FolderViewTreeView::onSortFilterChanged,
|
|
||||||
Qt::UniqueConnection);
|
|
||||||
onSortFilterChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewTreeView::mousePressEvent(QMouseEvent* event) {
|
|
||||||
QTreeView::mousePressEvent(event);
|
|
||||||
static_cast<FolderView*>(parent())->childMousePressEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewTreeView::dragEnterEvent(QDragEnterEvent* event) {
|
|
||||||
QTreeView::dragEnterEvent(event);
|
|
||||||
static_cast<FolderView*>(parent())->childDragEnterEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewTreeView::dragLeaveEvent(QDragLeaveEvent* e) {
|
|
||||||
QTreeView::dragLeaveEvent(e);
|
|
||||||
static_cast<FolderView*>(parent())->childDragLeaveEvent(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewTreeView::dragMoveEvent(QDragMoveEvent* e) {
|
|
||||||
QTreeView::dragMoveEvent(e);
|
|
||||||
static_cast<FolderView*>(parent())->childDragMoveEvent(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewTreeView::dropEvent(QDropEvent* e) {
|
|
||||||
static_cast<FolderView*>(parent())->childDropEvent(e);
|
|
||||||
QTreeView::dropEvent(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// the default list mode of QListView handles column widths
|
|
||||||
// very badly (worse than gtk+) and it's not very flexible.
|
|
||||||
// so, let's handle column widths outselves.
|
|
||||||
void FolderViewTreeView::layoutColumns() {
|
|
||||||
// qDebug("layoutColumns");
|
|
||||||
if(!model())
|
|
||||||
return;
|
|
||||||
doingLayout_ = true;
|
|
||||||
QHeaderView* headerView = header();
|
|
||||||
// the width that's available for showing the columns.
|
|
||||||
int availWidth = viewport()->contentsRect().width();
|
|
||||||
int desiredWidth = 0;
|
|
||||||
|
|
||||||
// get the width that every column want
|
|
||||||
int numCols = headerView->count();
|
|
||||||
if(numCols > 0) {
|
|
||||||
int* widths = new int[numCols]; // array to store the widths every column needs
|
|
||||||
int column;
|
|
||||||
for(column = 0; column < numCols; ++column) {
|
|
||||||
int columnId = headerView->logicalIndex(column);
|
|
||||||
// get the size that the column needs
|
|
||||||
widths[column] = sizeHintForColumn(columnId);
|
|
||||||
// compute the total width needed
|
|
||||||
desiredWidth += widths[column];
|
|
||||||
}
|
|
||||||
|
|
||||||
int filenameColumn = headerView->visualIndex(FolderModel::ColumnFileName);
|
|
||||||
// if the total witdh we want exceeds the available space
|
|
||||||
if(desiredWidth > availWidth) {
|
|
||||||
// Compute the width available for the filename column
|
|
||||||
int filenameAvailWidth = availWidth - desiredWidth + widths[filenameColumn];
|
|
||||||
|
|
||||||
// Compute the minimum acceptable width for the filename column
|
|
||||||
int filenameMinWidth = qMin(200, sizeHintForColumn(filenameColumn));
|
|
||||||
|
|
||||||
if (filenameAvailWidth > filenameMinWidth) {
|
|
||||||
// Shrink the filename column to the available width
|
|
||||||
widths[filenameColumn] = filenameAvailWidth;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Set the filename column to its minimum width
|
|
||||||
widths[filenameColumn] = filenameMinWidth;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Fill the extra available space with the filename column
|
|
||||||
widths[filenameColumn] += availWidth - desiredWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
// really do the resizing for every column
|
|
||||||
for(int column = 0; column < numCols; ++column) {
|
|
||||||
headerView->resizeSection(column, widths[column]);
|
|
||||||
}
|
|
||||||
delete []widths;
|
|
||||||
}
|
|
||||||
doingLayout_ = false;
|
|
||||||
|
|
||||||
if(layoutTimer_) {
|
|
||||||
delete layoutTimer_;
|
|
||||||
layoutTimer_ = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewTreeView::resizeEvent(QResizeEvent* event) {
|
|
||||||
QAbstractItemView::resizeEvent(event);
|
|
||||||
// prevent endless recursion.
|
|
||||||
// When manually resizing columns, at the point where a horizontal scroll
|
|
||||||
// bar has to be inserted or removed, the vertical size changes, a resize
|
|
||||||
// event occurs and the column headers are flickering badly if the column
|
|
||||||
// layout is modified at this point. Therefore only layout the columns if
|
|
||||||
// the horizontal size changes.
|
|
||||||
if(!doingLayout_ && event->size().width() != event->oldSize().width())
|
|
||||||
layoutColumns(); // layoutColumns() also triggers resizeEvent
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewTreeView::rowsInserted(const QModelIndex& parent, int start, int end) {
|
|
||||||
QTreeView::rowsInserted(parent, start, end);
|
|
||||||
queueLayoutColumns();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewTreeView::rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) {
|
|
||||||
QTreeView::rowsAboutToBeRemoved(parent, start, end);
|
|
||||||
queueLayoutColumns();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewTreeView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight) {
|
|
||||||
QTreeView::dataChanged(topLeft, bottomRight);
|
|
||||||
// FIXME: this will be very inefficient
|
|
||||||
// queueLayoutColumns();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewTreeView::reset() {
|
|
||||||
// Sometimes when the content of the model is radically changed, Qt does reset()
|
|
||||||
// on the model rather than doing large amount of insertion and deletion.
|
|
||||||
// This is for performance reason so in this case rowsInserted() and rowsAboutToBeRemoved()
|
|
||||||
// might not be called. Hence we also have to re-layout the columns when the model is reset.
|
|
||||||
// This fixes bug #190
|
|
||||||
// https://github.com/lxde/pcmanfm-qt/issues/190
|
|
||||||
QTreeView::reset();
|
|
||||||
queueLayoutColumns();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewTreeView::queueLayoutColumns() {
|
|
||||||
// qDebug("queueLayoutColumns");
|
|
||||||
if(!layoutTimer_) {
|
|
||||||
layoutTimer_ = new QTimer();
|
|
||||||
layoutTimer_->setSingleShot(true);
|
|
||||||
layoutTimer_->setInterval(0);
|
|
||||||
connect(layoutTimer_, &QTimer::timeout, this, &FolderViewTreeView::layoutColumns);
|
|
||||||
}
|
|
||||||
layoutTimer_->start();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewTreeView::mouseReleaseEvent(QMouseEvent* event) {
|
|
||||||
bool activationWasAllowed = activationAllowed_;
|
|
||||||
if ((!style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, NULL, this)) || (event->button() != Qt::LeftButton)) {
|
|
||||||
activationAllowed_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QTreeView::mouseReleaseEvent(event);
|
|
||||||
|
|
||||||
activationAllowed_ = activationWasAllowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewTreeView::mouseDoubleClickEvent(QMouseEvent* event) {
|
|
||||||
bool activationWasAllowed = activationAllowed_;
|
|
||||||
if ((style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, NULL, this)) || (event->button() != Qt::LeftButton)) {
|
|
||||||
activationAllowed_ = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
QTreeView::mouseDoubleClickEvent(event);
|
|
||||||
|
|
||||||
activationAllowed_ = activationWasAllowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewTreeView::activation(const QModelIndex &index) {
|
|
||||||
if (activationAllowed_) {
|
|
||||||
Q_EMIT activatedFiltered(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderViewTreeView::onSortFilterChanged() {
|
|
||||||
if(QSortFilterProxyModel* proxyModel = qobject_cast<QSortFilterProxyModel*>(model())) {
|
|
||||||
header()->setSortIndicatorShown(true);
|
|
||||||
header()->setSortIndicator(proxyModel->sortColumn(), proxyModel->sortOrder());
|
|
||||||
if (!isSortingEnabled()) {
|
|
||||||
setSortingEnabled(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
FolderView::FolderView(ViewMode _mode, QWidget* parent):
|
|
||||||
QWidget(parent),
|
|
||||||
view(NULL),
|
|
||||||
mode((ViewMode)0),
|
|
||||||
autoSelectionDelay_(600),
|
|
||||||
autoSelectionTimer_(NULL),
|
|
||||||
selChangedTimer_(NULL),
|
|
||||||
fileLauncher_(NULL),
|
|
||||||
model_(NULL) {
|
|
||||||
|
|
||||||
iconSize_[IconMode - FirstViewMode] = QSize(48, 48);
|
|
||||||
iconSize_[CompactMode - FirstViewMode] = QSize(24, 24);
|
|
||||||
iconSize_[ThumbnailMode - FirstViewMode] = QSize(128, 128);
|
|
||||||
iconSize_[DetailedListMode - FirstViewMode] = QSize(24, 24);
|
|
||||||
|
|
||||||
QVBoxLayout* layout = new QVBoxLayout();
|
|
||||||
layout->setMargin(0);
|
|
||||||
setLayout(layout);
|
|
||||||
|
|
||||||
setViewMode(_mode);
|
|
||||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
|
||||||
|
|
||||||
connect(this, &FolderView::clicked, this, &FolderView::onFileClicked);
|
|
||||||
}
|
|
||||||
|
|
||||||
FolderView::~FolderView() {
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderView::onItemActivated(QModelIndex index) {
|
|
||||||
if(index.isValid() && index.model()) {
|
|
||||||
QVariant data = index.model()->data(index, FolderModel::FileInfoRole);
|
|
||||||
FmFileInfo* info = (FmFileInfo*)data.value<void*>();
|
|
||||||
if(info) {
|
|
||||||
if (!(QApplication::keyboardModifiers() & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier))) {
|
|
||||||
Q_EMIT clicked(ActivatedClick, info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderView::onSelChangedTimeout() {
|
|
||||||
selChangedTimer_->deleteLater();
|
|
||||||
selChangedTimer_ = NULL;
|
|
||||||
|
|
||||||
QItemSelectionModel* selModel = selectionModel();
|
|
||||||
int nSel = 0;
|
|
||||||
if(viewMode() == DetailedListMode)
|
|
||||||
nSel = selModel->selectedRows().count();
|
|
||||||
else {
|
|
||||||
nSel = selModel->selectedIndexes().count();
|
|
||||||
}
|
|
||||||
// qDebug()<<"selected:" << nSel;
|
|
||||||
Q_EMIT selChanged(nSel); // FIXME: this is inefficient
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderView::onSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected) {
|
|
||||||
// It's possible that the selected items change too often and this slot gets called for thousands of times.
|
|
||||||
// For example, when you select thousands of files and delete them, we will get one selectionChanged() event
|
|
||||||
// for every deleted file. So, we use a timer to delay the handling to avoid too frequent updates of the UI.
|
|
||||||
if(!selChangedTimer_) {
|
|
||||||
selChangedTimer_ = new QTimer(this);
|
|
||||||
selChangedTimer_->setSingleShot(true);
|
|
||||||
connect(selChangedTimer_, &QTimer::timeout, this, &FolderView::onSelChangedTimeout);
|
|
||||||
selChangedTimer_->start(200);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void FolderView::setViewMode(ViewMode _mode) {
|
|
||||||
if(_mode == mode) // if it's the same more, ignore
|
|
||||||
return;
|
|
||||||
// FIXME: retain old selection
|
|
||||||
|
|
||||||
// since only detailed list mode uses QTreeView, and others
|
|
||||||
// all use QListView, it's wise to preserve QListView when possible.
|
|
||||||
bool recreateView = false;
|
|
||||||
if(view && (mode == DetailedListMode || _mode == DetailedListMode)) {
|
|
||||||
delete view; // FIXME: no virtual dtor?
|
|
||||||
view = NULL;
|
|
||||||
recreateView = true;
|
|
||||||
}
|
|
||||||
mode = _mode;
|
|
||||||
QSize iconSize = iconSize_[mode - FirstViewMode];
|
|
||||||
|
|
||||||
if(mode == DetailedListMode) {
|
|
||||||
FolderViewTreeView* treeView = new FolderViewTreeView(this);
|
|
||||||
connect(treeView, &FolderViewTreeView::activatedFiltered, this, &FolderView::onItemActivated);
|
|
||||||
|
|
||||||
view = treeView;
|
|
||||||
treeView->setItemsExpandable(false);
|
|
||||||
treeView->setRootIsDecorated(false);
|
|
||||||
treeView->setAllColumnsShowFocus(false);
|
|
||||||
|
|
||||||
// set our own custom delegate
|
|
||||||
FolderItemDelegate* delegate = new FolderItemDelegate(treeView);
|
|
||||||
treeView->setItemDelegateForColumn(FolderModel::ColumnFileName, delegate);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
FolderViewListView* listView;
|
|
||||||
if(view)
|
|
||||||
listView = static_cast<FolderViewListView*>(view);
|
|
||||||
else {
|
|
||||||
listView = new FolderViewListView(this);
|
|
||||||
connect(listView, &FolderViewListView::activatedFiltered, this, &FolderView::onItemActivated);
|
|
||||||
view = listView;
|
|
||||||
}
|
|
||||||
// set our own custom delegate
|
|
||||||
FolderItemDelegate* delegate = new FolderItemDelegate(listView);
|
|
||||||
listView->setItemDelegateForColumn(FolderModel::ColumnFileName, delegate);
|
|
||||||
// FIXME: should we expose the delegate?
|
|
||||||
listView->setMovement(QListView::Static);
|
|
||||||
listView->setResizeMode(QListView::Adjust);
|
|
||||||
listView->setWrapping(true);
|
|
||||||
switch(mode) {
|
|
||||||
case IconMode: {
|
|
||||||
listView->setViewMode(QListView::IconMode);
|
|
||||||
listView->setWordWrap(true);
|
|
||||||
listView->setFlow(QListView::LeftToRight);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case CompactMode: {
|
|
||||||
listView->setViewMode(QListView::ListMode);
|
|
||||||
listView->setWordWrap(false);
|
|
||||||
listView->setFlow(QListView::QListView::TopToBottom);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ThumbnailMode: {
|
|
||||||
listView->setViewMode(QListView::IconMode);
|
|
||||||
listView->setWordWrap(true);
|
|
||||||
listView->setFlow(QListView::LeftToRight);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:;
|
|
||||||
}
|
|
||||||
updateGridSize();
|
|
||||||
}
|
|
||||||
if(view) {
|
|
||||||
// we have to install the event filter on the viewport instead of the view itself.
|
|
||||||
view->viewport()->installEventFilter(this);
|
|
||||||
// we want the QEvent::HoverMove event for single click + auto-selection support
|
|
||||||
view->viewport()->setAttribute(Qt::WA_Hover, true);
|
|
||||||
view->setContextMenuPolicy(Qt::NoContextMenu); // defer the context menu handling to parent widgets
|
|
||||||
view->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
|
||||||
view->setIconSize(iconSize);
|
|
||||||
|
|
||||||
view->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
|
||||||
layout()->addWidget(view);
|
|
||||||
|
|
||||||
// enable dnd
|
|
||||||
view->setDragEnabled(true);
|
|
||||||
view->setAcceptDrops(true);
|
|
||||||
view->setDragDropMode(QAbstractItemView::DragDrop);
|
|
||||||
view->setDropIndicatorShown(true);
|
|
||||||
|
|
||||||
if(model_) {
|
|
||||||
// FIXME: preserve selections
|
|
||||||
model_->setThumbnailSize(iconSize.width());
|
|
||||||
view->setModel(model_);
|
|
||||||
if(recreateView)
|
|
||||||
connect(view->selectionModel(), &QItemSelectionModel::selectionChanged, this, &FolderView::onSelectionChanged);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// set proper grid size for the QListView based on current view mode, icon size, and font size.
|
|
||||||
void FolderView::updateGridSize() {
|
|
||||||
if(mode == DetailedListMode || !view)
|
|
||||||
return;
|
|
||||||
FolderViewListView* listView = static_cast<FolderViewListView*>(view);
|
|
||||||
QSize icon = iconSize(mode); // size of the icon
|
|
||||||
QFontMetrics fm = fontMetrics(); // size of current font
|
|
||||||
QSize grid; // the final grid size
|
|
||||||
switch(mode) {
|
|
||||||
case IconMode:
|
|
||||||
case ThumbnailMode: {
|
|
||||||
// NOTE by PCMan about finding the optimal text label size:
|
|
||||||
// The average filename length on my root filesystem is roughly 18-20 chars.
|
|
||||||
// So, a reasonable size for the text label is about 10 chars each line since string of this length
|
|
||||||
// can be shown in two lines. If you consider word wrap, then the result is around 10 chars per word.
|
|
||||||
// In average, 10 char per line should be enough to display a "word" in the filename without breaking.
|
|
||||||
// The values can be estimated with this command:
|
|
||||||
// > find / | xargs basename -a | sed -e s'/[_-]/ /g' | wc -mcw
|
|
||||||
// However, this average only applies to English. For some Asian characters, such as Chinese chars,
|
|
||||||
// each char actually takes doubled space. To be safe, we use 13 chars per line x average char width
|
|
||||||
// to get a nearly optimal width for the text label. As most of the filenames have less than 40 chars
|
|
||||||
// 13 chars x 3 lines should be enough to show the full filenames for most files.
|
|
||||||
int textWidth = fm.averageCharWidth() * 12 + 4; // add 2 px padding for left and right border
|
|
||||||
int textHeight = fm.height() * 3 + 4; // add 2 px padding for top and bottom border
|
|
||||||
grid.setWidth(qMax(icon.width(), textWidth) + 8); // add a margin 4 px for every cell
|
|
||||||
grid.setHeight(icon.height() + textHeight + 8); // add a margin 4 px for every cell
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
; // do not use grid size
|
|
||||||
}
|
|
||||||
listView->setGridSize(grid);
|
|
||||||
FolderItemDelegate* delegate = static_cast<FolderItemDelegate*>(listView->itemDelegateForColumn(FolderModel::ColumnFileName));
|
|
||||||
delegate->setGridSize(grid);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderView::setIconSize(ViewMode mode, QSize size) {
|
|
||||||
Q_ASSERT(mode >= FirstViewMode && mode <= LastViewMode);
|
|
||||||
iconSize_[mode - FirstViewMode] = size;
|
|
||||||
if(viewMode() == mode) {
|
|
||||||
view->setIconSize(size);
|
|
||||||
if(model_)
|
|
||||||
model_->setThumbnailSize(size.width());
|
|
||||||
updateGridSize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QSize FolderView::iconSize(ViewMode mode) const {
|
|
||||||
Q_ASSERT(mode >= FirstViewMode && mode <= LastViewMode);
|
|
||||||
return iconSize_[mode - FirstViewMode];
|
|
||||||
}
|
|
||||||
|
|
||||||
FolderView::ViewMode FolderView::viewMode() const {
|
|
||||||
return mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderView::setAutoSelectionDelay(int delay) {
|
|
||||||
autoSelectionDelay_ = delay;
|
|
||||||
}
|
|
||||||
|
|
||||||
QAbstractItemView* FolderView::childView() const {
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
ProxyFolderModel* FolderView::model() const {
|
|
||||||
return model_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderView::setModel(ProxyFolderModel* model) {
|
|
||||||
if(view) {
|
|
||||||
view->setModel(model);
|
|
||||||
QSize iconSize = iconSize_[mode - FirstViewMode];
|
|
||||||
model->setThumbnailSize(iconSize.width());
|
|
||||||
if(view->selectionModel())
|
|
||||||
connect(view->selectionModel(), &QItemSelectionModel::selectionChanged, this, &FolderView::onSelectionChanged);
|
|
||||||
}
|
|
||||||
if(model_)
|
|
||||||
delete model_;
|
|
||||||
model_ = model;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FolderView::event(QEvent* event) {
|
|
||||||
switch(event->type()) {
|
|
||||||
case QEvent::StyleChange:
|
|
||||||
break;
|
|
||||||
case QEvent::FontChange:
|
|
||||||
updateGridSize();
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
return QWidget::event(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderView::contextMenuEvent(QContextMenuEvent* event) {
|
|
||||||
QWidget::contextMenuEvent(event);
|
|
||||||
QPoint pos = event->pos();
|
|
||||||
QPoint view_pos = view->mapFromParent(pos);
|
|
||||||
QPoint viewport_pos = view->viewport()->mapFromParent(view_pos);
|
|
||||||
emitClickedAt(ContextMenuClick, viewport_pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderView::childMousePressEvent(QMouseEvent* event) {
|
|
||||||
// called from mousePressEvent() of child view
|
|
||||||
Qt::MouseButton button = event->button();
|
|
||||||
if(button == Qt::MiddleButton) {
|
|
||||||
emitClickedAt(MiddleClick, event->pos());
|
|
||||||
} else if (button == Qt::BackButton) {
|
|
||||||
Q_EMIT clickedBack();
|
|
||||||
} else if (button == Qt::ForwardButton) {
|
|
||||||
Q_EMIT clickedForward();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderView::emitClickedAt(ClickType type, const QPoint& pos) {
|
|
||||||
// indexAt() needs a point in "viewport" coordinates.
|
|
||||||
QModelIndex index = view->indexAt(pos);
|
|
||||||
if(index.isValid()) {
|
|
||||||
QVariant data = index.data(FolderModel::FileInfoRole);
|
|
||||||
FmFileInfo* info = reinterpret_cast<FmFileInfo*>(data.value<void*>());
|
|
||||||
Q_EMIT clicked(type, info);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// FIXME: should we show popup menu for the selected files instead
|
|
||||||
// if there are selected files?
|
|
||||||
if(type == ContextMenuClick) {
|
|
||||||
// clear current selection if clicked outside selected files
|
|
||||||
view->clearSelection();
|
|
||||||
Q_EMIT clicked(type, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndexList FolderView::selectedRows(int column) const {
|
|
||||||
QItemSelectionModel* selModel = selectionModel();
|
|
||||||
if(selModel) {
|
|
||||||
return selModel->selectedRows(column);
|
|
||||||
}
|
|
||||||
return QModelIndexList();
|
|
||||||
}
|
|
||||||
|
|
||||||
// This returns all selected "cells", which means all cells of the same row are returned.
|
|
||||||
QModelIndexList FolderView::selectedIndexes() const {
|
|
||||||
QItemSelectionModel* selModel = selectionModel();
|
|
||||||
if(selModel) {
|
|
||||||
return selModel->selectedIndexes();
|
|
||||||
}
|
|
||||||
return QModelIndexList();
|
|
||||||
}
|
|
||||||
|
|
||||||
QItemSelectionModel* FolderView::selectionModel() const {
|
|
||||||
return view ? view->selectionModel() : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
FmPathList* FolderView::selectedFilePaths() const {
|
|
||||||
if(model_) {
|
|
||||||
QModelIndexList selIndexes = mode == DetailedListMode ? selectedRows() : selectedIndexes();
|
|
||||||
if(!selIndexes.isEmpty()) {
|
|
||||||
FmPathList* paths = fm_path_list_new();
|
|
||||||
QModelIndexList::const_iterator it;
|
|
||||||
for(it = selIndexes.begin(); it != selIndexes.end(); ++it) {
|
|
||||||
FmFileInfo* file = model_->fileInfoFromIndex(*it);
|
|
||||||
fm_path_list_push_tail(paths, fm_file_info_get_path(file));
|
|
||||||
}
|
|
||||||
return paths;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
FmFileInfoList* FolderView::selectedFiles() const {
|
|
||||||
if(model_) {
|
|
||||||
QModelIndexList selIndexes = mode == DetailedListMode ? selectedRows() : selectedIndexes();
|
|
||||||
if(!selIndexes.isEmpty()) {
|
|
||||||
FmFileInfoList* files = fm_file_info_list_new();
|
|
||||||
QModelIndexList::const_iterator it;
|
|
||||||
for(it = selIndexes.constBegin(); it != selIndexes.constEnd(); ++it) {
|
|
||||||
FmFileInfo* file = model_->fileInfoFromIndex(*it);
|
|
||||||
fm_file_info_list_push_tail(files, file);
|
|
||||||
}
|
|
||||||
return files;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderView::selectAll() {
|
|
||||||
if(mode == DetailedListMode)
|
|
||||||
view->selectAll();
|
|
||||||
else {
|
|
||||||
// NOTE: By default QListView::selectAll() selects all columns in the model.
|
|
||||||
// However, QListView only show the first column. Normal selection by mouse
|
|
||||||
// can only select the first column of every row. I consider this discripancy yet
|
|
||||||
// another design flaw of Qt. To make them consistent, we do it ourselves by only
|
|
||||||
// selecting the first column of every row and do not select all columns as Qt does.
|
|
||||||
// This will trigger one selectionChanged event per row, which is very inefficient,
|
|
||||||
// but we have no other choices to workaround the Qt bug.
|
|
||||||
// I'll report a Qt bug for this later.
|
|
||||||
if(model_) {
|
|
||||||
int rowCount = model_->rowCount();
|
|
||||||
for(int row = 0; row < rowCount; ++row) {
|
|
||||||
QModelIndex index = model_->index(row, 0);
|
|
||||||
selectionModel()->select(index, QItemSelectionModel::Select);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderView::invertSelection() {
|
|
||||||
if(model_) {
|
|
||||||
QItemSelectionModel* selModel = view->selectionModel();
|
|
||||||
int rows = model_->rowCount();
|
|
||||||
QItemSelectionModel::SelectionFlags flags = QItemSelectionModel::Toggle;
|
|
||||||
if(mode == DetailedListMode)
|
|
||||||
flags |= QItemSelectionModel::Rows;
|
|
||||||
for(int row = 0; row < rows; ++row) {
|
|
||||||
QModelIndex index = model_->index(row, 0);
|
|
||||||
selModel->select(index, flags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderView::childDragEnterEvent(QDragEnterEvent* event) {
|
|
||||||
qDebug("drag enter");
|
|
||||||
if(event->mimeData()->hasFormat("text/uri-list")) {
|
|
||||||
event->accept();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
event->ignore();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderView::childDragLeaveEvent(QDragLeaveEvent* e) {
|
|
||||||
qDebug("drag leave");
|
|
||||||
e->accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderView::childDragMoveEvent(QDragMoveEvent* e) {
|
|
||||||
qDebug("drag move");
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderView::childDropEvent(QDropEvent* e) {
|
|
||||||
qDebug("drop");
|
|
||||||
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.
|
|
||||||
Qt::DropAction action = DndActionMenu::askUser(QCursor::pos());
|
|
||||||
e->setDropAction(action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FolderView::eventFilter(QObject* watched, QEvent* event) {
|
|
||||||
// NOTE: Instead of simply filtering the drag and drop events of the child view in
|
|
||||||
// the event filter, we overrided each event handler virtual methods in
|
|
||||||
// both QListView and QTreeView and added some childXXXEvent() callbacks.
|
|
||||||
// We did this because of a design flaw of Qt.
|
|
||||||
// All QAbstractScrollArea derived widgets, including QAbstractItemView
|
|
||||||
// contains an internal child widget, which is called a viewport.
|
|
||||||
// The events actually comes from the child viewport, not the parent view itself.
|
|
||||||
// Qt redirects the events of viewport to the viewportEvent() method of
|
|
||||||
// QAbstractScrollArea and let the parent widget handle the events.
|
|
||||||
// Qt implemented this using a event filter installed on the child viewport widget.
|
|
||||||
// That means, when we try to install an event filter on the viewport,
|
|
||||||
// there is already a filter installed by Qt which will be called before ours.
|
|
||||||
// So we can never intercept the event handling of QAbstractItemView by using a filter.
|
|
||||||
// That's why we override respective virtual methods for different events.
|
|
||||||
if(view && watched == view->viewport()) {
|
|
||||||
switch(event->type()) {
|
|
||||||
case QEvent::HoverMove:
|
|
||||||
// activate items on single click
|
|
||||||
if(style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)) {
|
|
||||||
QHoverEvent* hoverEvent = static_cast<QHoverEvent*>(event);
|
|
||||||
QModelIndex index = view->indexAt(hoverEvent->pos()); // find out the hovered item
|
|
||||||
if(index.isValid()) { // change the cursor to a hand when hovering on an item
|
|
||||||
setCursor(Qt::PointingHandCursor);
|
|
||||||
if(!selectionModel()->hasSelection())
|
|
||||||
selectionModel()->setCurrentIndex(index, QItemSelectionModel::Current);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
setCursor(Qt::ArrowCursor);
|
|
||||||
// turn on auto-selection for hovered item when single click mode is used.
|
|
||||||
if(autoSelectionDelay_ > 0 && model_) {
|
|
||||||
if(!autoSelectionTimer_) {
|
|
||||||
autoSelectionTimer_ = new QTimer(this);
|
|
||||||
connect(autoSelectionTimer_, &QTimer::timeout, this, &FolderView::onAutoSelectionTimeout);
|
|
||||||
lastAutoSelectionIndex_ = QModelIndex();
|
|
||||||
}
|
|
||||||
autoSelectionTimer_->start(autoSelectionDelay_);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case QEvent::HoverLeave:
|
|
||||||
if(style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick))
|
|
||||||
setCursor(Qt::ArrowCursor);
|
|
||||||
break;
|
|
||||||
case QEvent::Wheel:
|
|
||||||
// This is to fix #85: Scrolling doesn't work in compact view
|
|
||||||
// Actually, I think it's the bug of Qt, not ours.
|
|
||||||
// When in compact mode, only the horizontal scroll bar is used and the vertical one is hidden.
|
|
||||||
// So, when a user scroll his mouse wheel, it's reasonable to scroll the horizontal scollbar.
|
|
||||||
// Qt does not implement such a simple feature, unfortunately.
|
|
||||||
// We do it by forwarding the scroll event in the viewport to the horizontal scrollbar.
|
|
||||||
// FIXME: if someday Qt supports this, we have to disable the workaround.
|
|
||||||
if(mode == CompactMode) {
|
|
||||||
QScrollBar* scroll = view->horizontalScrollBar();
|
|
||||||
if(scroll) {
|
|
||||||
QApplication::sendEvent(scroll, event);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return QObject::eventFilter(watched, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
// this slot handles auto-selection of items.
|
|
||||||
void FolderView::onAutoSelectionTimeout() {
|
|
||||||
if(QApplication::mouseButtons() != Qt::NoButton)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Qt::KeyboardModifiers mods = QApplication::keyboardModifiers();
|
|
||||||
QPoint pos = view->viewport()->mapFromGlobal(QCursor::pos()); // convert to viewport coordinates
|
|
||||||
QModelIndex index = view->indexAt(pos); // find out the hovered item
|
|
||||||
QItemSelectionModel::SelectionFlags flags = (mode == DetailedListMode ? QItemSelectionModel::Rows : QItemSelectionModel::NoUpdate);
|
|
||||||
QItemSelectionModel* selModel = view->selectionModel();
|
|
||||||
|
|
||||||
if(mods & Qt::ControlModifier) { // Ctrl key is pressed
|
|
||||||
if(selModel->isSelected(index) && index != lastAutoSelectionIndex_) {
|
|
||||||
// unselect a previously selected item
|
|
||||||
selModel->select(index, flags|QItemSelectionModel::Deselect);
|
|
||||||
lastAutoSelectionIndex_ = QModelIndex();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// select an unselected item
|
|
||||||
selModel->select(index, flags|QItemSelectionModel::Select);
|
|
||||||
lastAutoSelectionIndex_ = index;
|
|
||||||
}
|
|
||||||
selModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate); // move the cursor
|
|
||||||
}
|
|
||||||
else if(mods & Qt::ShiftModifier) { // Shift key is pressed
|
|
||||||
// select all items between current index and the hovered index.
|
|
||||||
QModelIndex current = selModel->currentIndex();
|
|
||||||
if(selModel->hasSelection() && current.isValid()) {
|
|
||||||
selModel->clear(); // clear old selection
|
|
||||||
selModel->setCurrentIndex(current, QItemSelectionModel::NoUpdate);
|
|
||||||
int begin = current.row();
|
|
||||||
int end = index.row();
|
|
||||||
if(begin > end)
|
|
||||||
qSwap(begin, end);
|
|
||||||
for(int row = begin; row <= end; ++row) {
|
|
||||||
QModelIndex sel = model_->index(row, 0);
|
|
||||||
selModel->select(sel, flags|QItemSelectionModel::Select);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else { // no items are selected, select the hovered item.
|
|
||||||
if(index.isValid()) {
|
|
||||||
selModel->select(index, flags|QItemSelectionModel::SelectCurrent);
|
|
||||||
selModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lastAutoSelectionIndex_ = index;
|
|
||||||
}
|
|
||||||
else if(mods == Qt::NoModifier) { // no modifier keys are pressed.
|
|
||||||
if(index.isValid()) {
|
|
||||||
// select the hovered item
|
|
||||||
view->clearSelection();
|
|
||||||
selModel->select(index, flags|QItemSelectionModel::SelectCurrent);
|
|
||||||
selModel->setCurrentIndex(index, QItemSelectionModel::NoUpdate);
|
|
||||||
}
|
|
||||||
lastAutoSelectionIndex_ = index;
|
|
||||||
}
|
|
||||||
|
|
||||||
autoSelectionTimer_->deleteLater();
|
|
||||||
autoSelectionTimer_ = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderView::onFileClicked(int type, FmFileInfo* fileInfo) {
|
|
||||||
if(type == ActivatedClick) {
|
|
||||||
if(fileLauncher_) {
|
|
||||||
GList* files = g_list_append(NULL, fileInfo);
|
|
||||||
fileLauncher_->launchFiles(NULL, files);
|
|
||||||
g_list_free(files);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(type == ContextMenuClick) {
|
|
||||||
FmPath* folderPath = NULL;
|
|
||||||
FmFileInfoList* files = selectedFiles();
|
|
||||||
if (files) {
|
|
||||||
FmFileInfo* first = fm_file_info_list_peek_head(files);
|
|
||||||
if (fm_file_info_list_get_length(files) == 1 && fm_file_info_is_dir(first))
|
|
||||||
folderPath = fm_file_info_get_path(first);
|
|
||||||
}
|
|
||||||
if (!folderPath)
|
|
||||||
folderPath = path();
|
|
||||||
QMenu* menu = NULL;
|
|
||||||
if(fileInfo) {
|
|
||||||
// show context menu
|
|
||||||
if (FmFileInfoList* files = selectedFiles()) {
|
|
||||||
Fm::FileMenu* fileMenu = new Fm::FileMenu(files, fileInfo, folderPath);
|
|
||||||
fileMenu->setFileLauncher(fileLauncher_);
|
|
||||||
prepareFileMenu(fileMenu);
|
|
||||||
fm_file_info_list_unref(files);
|
|
||||||
menu = fileMenu;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Fm::FolderMenu* folderMenu = new Fm::FolderMenu(this);
|
|
||||||
prepareFolderMenu(folderMenu);
|
|
||||||
menu = folderMenu;
|
|
||||||
}
|
|
||||||
if (menu) {
|
|
||||||
menu->exec(QCursor::pos());
|
|
||||||
delete menu;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderView::prepareFileMenu(FileMenu* menu) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void FolderView::prepareFolderMenu(FolderMenu* menu) {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace Fm
|
|
@ -1,169 +0,0 @@
|
|||||||
/*
|
|
||||||
<one line to give the library's name and an idea of what it does.>
|
|
||||||
Copyright (C) 2012 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_FOLDERVIEW_H
|
|
||||||
#define FM_FOLDERVIEW_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QWidget>
|
|
||||||
#include <QListView>
|
|
||||||
#include <QTreeView>
|
|
||||||
#include <QMouseEvent>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
#include "foldermodel.h"
|
|
||||||
#include "proxyfoldermodel.h"
|
|
||||||
|
|
||||||
class QTimer;
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class FileMenu;
|
|
||||||
class FolderMenu;
|
|
||||||
class FileLauncher;
|
|
||||||
class FolderViewStyle;
|
|
||||||
|
|
||||||
class LIBFM_QT_API FolderView : public QWidget {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
enum ViewMode {
|
|
||||||
FirstViewMode = 1,
|
|
||||||
IconMode = FirstViewMode,
|
|
||||||
CompactMode,
|
|
||||||
DetailedListMode,
|
|
||||||
ThumbnailMode,
|
|
||||||
LastViewMode = ThumbnailMode,
|
|
||||||
NumViewModes = (LastViewMode - FirstViewMode + 1)
|
|
||||||
};
|
|
||||||
|
|
||||||
enum ClickType {
|
|
||||||
ActivatedClick,
|
|
||||||
MiddleClick,
|
|
||||||
ContextMenuClick
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
friend class FolderViewTreeView;
|
|
||||||
friend class FolderViewListView;
|
|
||||||
|
|
||||||
explicit FolderView(ViewMode _mode = IconMode, QWidget* parent = 0);
|
|
||||||
virtual ~FolderView();
|
|
||||||
|
|
||||||
void setViewMode(ViewMode _mode);
|
|
||||||
ViewMode viewMode() const;
|
|
||||||
|
|
||||||
void setIconSize(ViewMode mode, QSize size);
|
|
||||||
QSize iconSize(ViewMode mode) const;
|
|
||||||
|
|
||||||
QAbstractItemView* childView() const;
|
|
||||||
|
|
||||||
ProxyFolderModel* model() const;
|
|
||||||
void setModel(ProxyFolderModel* _model);
|
|
||||||
|
|
||||||
FmFolder* folder() {
|
|
||||||
return model_ ? static_cast<FolderModel*>(model_->sourceModel())->folder() : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
FmFileInfo* folderInfo() {
|
|
||||||
FmFolder* _folder = folder();
|
|
||||||
return _folder ? fm_folder_get_info(_folder) : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
FmPath* path() {
|
|
||||||
FmFolder* _folder = folder();
|
|
||||||
return _folder ? fm_folder_get_path(_folder) : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
QItemSelectionModel* selectionModel() const;
|
|
||||||
FmFileInfoList* selectedFiles() const;
|
|
||||||
FmPathList* selectedFilePaths() const;
|
|
||||||
|
|
||||||
void selectAll();
|
|
||||||
|
|
||||||
void invertSelection();
|
|
||||||
|
|
||||||
void setFileLauncher(FileLauncher* launcher) {
|
|
||||||
fileLauncher_ = launcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileLauncher* fileLauncher() {
|
|
||||||
return fileLauncher_;
|
|
||||||
}
|
|
||||||
|
|
||||||
int autoSelectionDelay() const {
|
|
||||||
return autoSelectionDelay_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setAutoSelectionDelay(int delay);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual bool event(QEvent* event);
|
|
||||||
virtual void contextMenuEvent(QContextMenuEvent* event);
|
|
||||||
virtual void childMousePressEvent(QMouseEvent* event);
|
|
||||||
virtual void childDragEnterEvent(QDragEnterEvent* event);
|
|
||||||
virtual void childDragMoveEvent(QDragMoveEvent* e);
|
|
||||||
virtual void childDragLeaveEvent(QDragLeaveEvent* e);
|
|
||||||
virtual void childDropEvent(QDropEvent* e);
|
|
||||||
|
|
||||||
void emitClickedAt(ClickType type, const QPoint& pos);
|
|
||||||
|
|
||||||
QModelIndexList selectedRows ( int column = 0 ) const;
|
|
||||||
QModelIndexList selectedIndexes() const;
|
|
||||||
|
|
||||||
virtual void prepareFileMenu(Fm::FileMenu* menu);
|
|
||||||
virtual void prepareFolderMenu(Fm::FolderMenu* menu);
|
|
||||||
|
|
||||||
virtual bool eventFilter(QObject* watched, QEvent* event);
|
|
||||||
|
|
||||||
void updateGridSize(); // called when view mode, icon size, or font size is changed
|
|
||||||
|
|
||||||
public Q_SLOTS:
|
|
||||||
void onItemActivated(QModelIndex index);
|
|
||||||
void onSelectionChanged(const QItemSelection & selected, const QItemSelection & deselected);
|
|
||||||
virtual void onFileClicked(int type, FmFileInfo* fileInfo);
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void onAutoSelectionTimeout();
|
|
||||||
void onSelChangedTimeout();
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void clicked(int type, FmFileInfo* file);
|
|
||||||
void clickedBack();
|
|
||||||
void clickedForward();
|
|
||||||
void selChanged(int n_sel);
|
|
||||||
void sortChanged();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
QAbstractItemView* view;
|
|
||||||
ProxyFolderModel* model_;
|
|
||||||
ViewMode mode;
|
|
||||||
QSize iconSize_[NumViewModes];
|
|
||||||
FileLauncher* fileLauncher_;
|
|
||||||
int autoSelectionDelay_;
|
|
||||||
QTimer* autoSelectionTimer_;
|
|
||||||
QModelIndex lastAutoSelectionIndex_;
|
|
||||||
QTimer* selChangedTimer_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_FOLDERVIEW_H
|
|
@ -1,109 +0,0 @@
|
|||||||
/*
|
|
||||||
<one line to give the library's name and an idea of what it does.>
|
|
||||||
Copyright (C) 2012 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_FOLDERVIEW_P_H
|
|
||||||
#define FM_FOLDERVIEW_P_H
|
|
||||||
|
|
||||||
#include <QListView>
|
|
||||||
#include <QTreeView>
|
|
||||||
#include <QMouseEvent>
|
|
||||||
#include "folderview.h"
|
|
||||||
|
|
||||||
class QTimer;
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
// override these classes for implementing FolderView
|
|
||||||
class FolderViewListView : public QListView {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
friend class FolderView;
|
|
||||||
FolderViewListView(QWidget* parent = 0);
|
|
||||||
virtual ~FolderViewListView();
|
|
||||||
virtual void startDrag(Qt::DropActions supportedActions);
|
|
||||||
virtual void mousePressEvent(QMouseEvent* event);
|
|
||||||
virtual void mouseReleaseEvent(QMouseEvent* event);
|
|
||||||
virtual void mouseDoubleClickEvent(QMouseEvent* event);
|
|
||||||
virtual void dragEnterEvent(QDragEnterEvent* event);
|
|
||||||
virtual void dragMoveEvent(QDragMoveEvent* e);
|
|
||||||
virtual void dragLeaveEvent(QDragLeaveEvent* e);
|
|
||||||
virtual void dropEvent(QDropEvent* e);
|
|
||||||
|
|
||||||
virtual QModelIndex indexAt(const QPoint & point) const;
|
|
||||||
|
|
||||||
inline void setPositionForIndex(const QPoint & position, const QModelIndex & index) {
|
|
||||||
QListView::setPositionForIndex(position, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline QRect rectForIndex(const QModelIndex & index) const {
|
|
||||||
return QListView::rectForIndex(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void activatedFiltered(const QModelIndex &index);
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void activation(const QModelIndex &index);
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool activationAllowed_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class FolderViewTreeView : public QTreeView {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
friend class FolderView;
|
|
||||||
FolderViewTreeView(QWidget* parent = 0);
|
|
||||||
virtual ~FolderViewTreeView();
|
|
||||||
virtual void setModel(QAbstractItemModel* model);
|
|
||||||
virtual void mousePressEvent(QMouseEvent* event);
|
|
||||||
virtual void mouseReleaseEvent(QMouseEvent* event);
|
|
||||||
virtual void mouseDoubleClickEvent(QMouseEvent* event);
|
|
||||||
virtual void dragEnterEvent(QDragEnterEvent* event);
|
|
||||||
virtual void dragMoveEvent(QDragMoveEvent* e);
|
|
||||||
virtual void dragLeaveEvent(QDragLeaveEvent* e);
|
|
||||||
virtual void dropEvent(QDropEvent* e);
|
|
||||||
|
|
||||||
virtual void rowsInserted(const QModelIndex& parent,int start, int end);
|
|
||||||
virtual void rowsAboutToBeRemoved(const QModelIndex& parent,int start, int end);
|
|
||||||
virtual void dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
|
|
||||||
virtual void reset();
|
|
||||||
|
|
||||||
virtual void resizeEvent(QResizeEvent* event);
|
|
||||||
void queueLayoutColumns();
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void activatedFiltered(const QModelIndex &index);
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void layoutColumns();
|
|
||||||
void activation(const QModelIndex &index);
|
|
||||||
void onSortFilterChanged();
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool doingLayout_;
|
|
||||||
QTimer* layoutTimer_;
|
|
||||||
bool activationAllowed_;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace Fm
|
|
||||||
|
|
||||||
#endif // FM_FOLDERVIEW_P_H
|
|
@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "fontbutton.h"
|
|
||||||
#include <QFontDialog>
|
|
||||||
#include <X11/X.h>
|
|
||||||
|
|
||||||
using namespace Fm;
|
|
||||||
|
|
||||||
FontButton::FontButton(QWidget* parent): QPushButton(parent) {
|
|
||||||
connect(this, &QPushButton::clicked, this, &FontButton::onClicked);
|
|
||||||
}
|
|
||||||
|
|
||||||
FontButton::~FontButton() {
|
|
||||||
}
|
|
||||||
|
|
||||||
void FontButton::onClicked() {
|
|
||||||
QFontDialog dlg(font_);
|
|
||||||
if(dlg.exec() == QDialog::Accepted) {
|
|
||||||
setFont(dlg.selectedFont());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FontButton::setFont(QFont font) {
|
|
||||||
font_ = font;
|
|
||||||
QString text = font.family();
|
|
||||||
if(font.bold()) {
|
|
||||||
text += " ";
|
|
||||||
text += tr("Bold");
|
|
||||||
}
|
|
||||||
if(font.italic()) {
|
|
||||||
text += " ";
|
|
||||||
text += tr("Italic");
|
|
||||||
}
|
|
||||||
text += QString(" %1").arg(font.pointSize());
|
|
||||||
setText(text);
|
|
||||||
Q_EMIT changed();
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef FM_FONTBUTTON_H
|
|
||||||
#define FM_FONTBUTTON_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QPushButton>
|
|
||||||
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class LIBFM_QT_API FontButton : public QPushButton {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit FontButton(QWidget* parent = 0);
|
|
||||||
virtual ~FontButton();
|
|
||||||
|
|
||||||
QFont font() {
|
|
||||||
return font_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setFont(QFont font);
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void changed();
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void onClicked();
|
|
||||||
|
|
||||||
private:
|
|
||||||
QFont font_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_FONTBUTTON_H
|
|
@ -1,143 +0,0 @@
|
|||||||
/*
|
|
||||||
<one line to give the library's name and an idea of what it does.>
|
|
||||||
Copyright (C) 2012 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 "icontheme.h"
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
#include <QList>
|
|
||||||
#include <QIcon>
|
|
||||||
#include <QtGlobal>
|
|
||||||
#include <QApplication>
|
|
||||||
#include <QDesktopWidget>
|
|
||||||
|
|
||||||
using namespace Fm;
|
|
||||||
|
|
||||||
static IconTheme* theIconTheme = NULL; // the global single instance of IconTheme.
|
|
||||||
static const char* fallbackNames[] = {"unknown", "application-octet-stream", NULL};
|
|
||||||
|
|
||||||
static void fmIconDataDestroy(gpointer data) {
|
|
||||||
QIcon* picon = reinterpret_cast<QIcon*>(data);
|
|
||||||
delete picon;
|
|
||||||
}
|
|
||||||
|
|
||||||
IconTheme::IconTheme():
|
|
||||||
currentThemeName_(QIcon::themeName()) {
|
|
||||||
// NOTE: only one instance is allowed
|
|
||||||
Q_ASSERT(theIconTheme == NULL);
|
|
||||||
Q_ASSERT(qApp != NULL); // QApplication should exists before contructing IconTheme.
|
|
||||||
|
|
||||||
theIconTheme = this;
|
|
||||||
fm_icon_set_user_data_destroy(reinterpret_cast<GDestroyNotify>(fmIconDataDestroy));
|
|
||||||
fallbackIcon_ = iconFromNames(fallbackNames);
|
|
||||||
|
|
||||||
// We need to get notified when there is a QEvent::StyleChange event so
|
|
||||||
// we can check if the current icon theme name is changed.
|
|
||||||
// To do this, we can filter QApplication object itself to intercept
|
|
||||||
// signals of all widgets, but this may be too inefficient.
|
|
||||||
// So, we only filter the events on QDesktopWidget instead.
|
|
||||||
qApp->desktop()->installEventFilter(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
IconTheme::~IconTheme() {
|
|
||||||
}
|
|
||||||
|
|
||||||
IconTheme* IconTheme::instance() {
|
|
||||||
return theIconTheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if the icon theme name is changed and emit "changed()" signal if any change is detected.
|
|
||||||
void IconTheme::checkChanged() {
|
|
||||||
if(QIcon::themeName() != theIconTheme->currentThemeName_) {
|
|
||||||
// if the icon theme is changed
|
|
||||||
theIconTheme->currentThemeName_ = QIcon::themeName();
|
|
||||||
// invalidate the cached data
|
|
||||||
fm_icon_reset_user_data_cache(fm_qdata_id);
|
|
||||||
|
|
||||||
theIconTheme->fallbackIcon_ = iconFromNames(fallbackNames);
|
|
||||||
Q_EMIT theIconTheme->changed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QIcon IconTheme::iconFromNames(const char* const* names) {
|
|
||||||
const gchar* const* name;
|
|
||||||
// qDebug("names: %p", names);
|
|
||||||
for(name = names; *name; ++name) {
|
|
||||||
// qDebug("icon name=%s", *name);
|
|
||||||
QString qname = *name;
|
|
||||||
QIcon qicon = QIcon::fromTheme(qname);
|
|
||||||
if(!qicon.isNull()) {
|
|
||||||
return qicon;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return QIcon();
|
|
||||||
}
|
|
||||||
|
|
||||||
QIcon IconTheme::convertFromGIcon(GIcon* gicon) {
|
|
||||||
if(G_IS_THEMED_ICON(gicon)) {
|
|
||||||
const gchar * const * names = g_themed_icon_get_names(G_THEMED_ICON(gicon));
|
|
||||||
QIcon icon = iconFromNames(names);
|
|
||||||
if(!icon.isNull())
|
|
||||||
return icon;
|
|
||||||
}
|
|
||||||
else if(G_IS_FILE_ICON(gicon)) {
|
|
||||||
GFile* file = g_file_icon_get_file(G_FILE_ICON(gicon));
|
|
||||||
char* fpath = g_file_get_path(file);
|
|
||||||
QString path = fpath;
|
|
||||||
g_free(fpath);
|
|
||||||
return QIcon(path);
|
|
||||||
}
|
|
||||||
return theIconTheme->fallbackIcon_;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//static
|
|
||||||
QIcon IconTheme::icon(FmIcon* fmicon) {
|
|
||||||
// check if we have a cached version
|
|
||||||
QIcon* picon = reinterpret_cast<QIcon*>(fm_icon_get_user_data(fmicon));
|
|
||||||
if(!picon) { // we don't have a cache yet
|
|
||||||
picon = new QIcon(); // what a waste!
|
|
||||||
*picon = convertFromGIcon(G_ICON(fmicon));
|
|
||||||
fm_icon_set_user_data(fmicon, picon); // store it in FmIcon
|
|
||||||
}
|
|
||||||
return *picon;
|
|
||||||
}
|
|
||||||
|
|
||||||
//static
|
|
||||||
QIcon IconTheme::icon(GIcon* gicon) {
|
|
||||||
if(G_IS_THEMED_ICON(gicon)) {
|
|
||||||
FmIcon* fmicon = fm_icon_from_gicon(gicon);
|
|
||||||
QIcon qicon = icon(fmicon);
|
|
||||||
fm_icon_unref(fmicon);
|
|
||||||
return qicon;
|
|
||||||
}
|
|
||||||
else if(G_IS_FILE_ICON(gicon)) {
|
|
||||||
// we do not map GFileIcon to FmIcon deliberately.
|
|
||||||
return convertFromGIcon(gicon);
|
|
||||||
}
|
|
||||||
return theIconTheme->fallbackIcon_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this method is called whenever there is an event on the QDesktopWidget object.
|
|
||||||
bool IconTheme::eventFilter(QObject* obj, QEvent* event) {
|
|
||||||
// we're only interested in the StyleChange event.
|
|
||||||
if(event->type() == QEvent::StyleChange) {
|
|
||||||
checkChanged(); // check if the icon theme is changed
|
|
||||||
}
|
|
||||||
return QObject::eventFilter(obj, event);
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
/*
|
|
||||||
<one line to give the library's name and an idea of what it does.>
|
|
||||||
Copyright (C) 2012 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_ICONTHEME_H
|
|
||||||
#define FM_ICONTHEME_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QIcon>
|
|
||||||
#include <QString>
|
|
||||||
#include "libfm/fm.h"
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
// NOTE:
|
|
||||||
// Qt seems to has its own QIcon pixmap caching mechanism internally.
|
|
||||||
// Besides, it also caches QIcon objects created by QIcon::fromTheme().
|
|
||||||
// So maybe we should not duplicate the work.
|
|
||||||
// See http://qt.gitorious.org/qt/qt/blobs/4.8/src/gui/image/qicon.cpp
|
|
||||||
// QPixmap QPixmapIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state).
|
|
||||||
// In addition, QPixmap is actually stored in X11 server, not client side.
|
|
||||||
// Hence maybe we should not cache too many pixmaps, I guess?
|
|
||||||
// Let's have Qt do its work and only translate GIcon to QIcon here.
|
|
||||||
|
|
||||||
// Nice article about QPixmap from KDE: http://techbase.kde.org/Development/Tutorials/Graphics/Performance
|
|
||||||
|
|
||||||
class LIBFM_QT_API IconTheme: public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
IconTheme();
|
|
||||||
~IconTheme();
|
|
||||||
|
|
||||||
static IconTheme* instance();
|
|
||||||
static QIcon icon(FmIcon* fmicon);
|
|
||||||
static QIcon icon(GIcon* gicon);
|
|
||||||
|
|
||||||
static void checkChanged(); // check if current icon theme name is changed
|
|
||||||
Q_SIGNALS:
|
|
||||||
void changed(); // emitted when the name of current icon theme is changed
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool eventFilter(QObject *obj, QEvent *event);
|
|
||||||
static QIcon convertFromGIcon(GIcon* gicon);
|
|
||||||
static QIcon iconFromNames(const char * const * names);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
QIcon fallbackIcon_;
|
|
||||||
QString currentThemeName_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_ICONTHEME_H
|
|
@ -1,12 +0,0 @@
|
|||||||
prefix=@CMAKE_INSTALL_PREFIX@
|
|
||||||
exec_prefix=${prefix}
|
|
||||||
libdir=${exec_prefix}/lib
|
|
||||||
includedir=${prefix}/include
|
|
||||||
|
|
||||||
Name: libfm-qt
|
|
||||||
Description: A Qt/glib/gio-based lib used to develop file managers providing some file management utilities. (This is a Qt port of the original libfm library)
|
|
||||||
URL: http://pcmanfm.sourceforge.net/
|
|
||||||
Requires: @REQUIRED_QT@ libfm >= 1.2.0
|
|
||||||
Version: @LIBFM_QT_VERSION@
|
|
||||||
Libs: -L${libdir} -lfm -l@LIBRARY_NAME@
|
|
||||||
Cflags: -I${includedir}
|
|
@ -1,79 +0,0 @@
|
|||||||
/*
|
|
||||||
<one line to give the library's name and an idea of what it does.>
|
|
||||||
Copyright (C) 2012 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 <libfm/fm.h>
|
|
||||||
#include "libfmqt.h"
|
|
||||||
#include <QLocale>
|
|
||||||
#include "icontheme.h"
|
|
||||||
#include "thumbnailloader.h"
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
struct LibFmQtData {
|
|
||||||
LibFmQtData();
|
|
||||||
~LibFmQtData();
|
|
||||||
|
|
||||||
IconTheme* iconTheme;
|
|
||||||
ThumbnailLoader* thumbnailLoader;
|
|
||||||
QTranslator translator;
|
|
||||||
int refCount;
|
|
||||||
Q_DISABLE_COPY(LibFmQtData)
|
|
||||||
};
|
|
||||||
|
|
||||||
static LibFmQtData* theLibFmData = NULL;
|
|
||||||
|
|
||||||
LibFmQtData::LibFmQtData(): refCount(1) {
|
|
||||||
#if !GLIB_CHECK_VERSION(2, 36, 0)
|
|
||||||
g_type_init();
|
|
||||||
#endif
|
|
||||||
fm_init(NULL);
|
|
||||||
// turn on glib debug message
|
|
||||||
// g_setenv("G_MESSAGES_DEBUG", "all", true);
|
|
||||||
iconTheme = new IconTheme();
|
|
||||||
thumbnailLoader = new ThumbnailLoader();
|
|
||||||
translator.load("libfm-qt_" + QLocale::system().name(), LIBFM_DATA_DIR "/translations");
|
|
||||||
}
|
|
||||||
|
|
||||||
LibFmQtData::~LibFmQtData() {
|
|
||||||
delete iconTheme;
|
|
||||||
delete thumbnailLoader;
|
|
||||||
fm_finalize();
|
|
||||||
}
|
|
||||||
|
|
||||||
LibFmQt::LibFmQt() {
|
|
||||||
if(!theLibFmData) {
|
|
||||||
theLibFmData = new LibFmQtData();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
++theLibFmData->refCount;
|
|
||||||
d = theLibFmData;
|
|
||||||
}
|
|
||||||
|
|
||||||
LibFmQt::~LibFmQt() {
|
|
||||||
if(--d->refCount == 0) {
|
|
||||||
delete d;
|
|
||||||
theLibFmData = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QTranslator* LibFmQt::translator() {
|
|
||||||
return &d->translator;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Fm
|
|
@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
<one line to give the library's name and an idea of what it does.>
|
|
||||||
Copyright (C) 2012 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_APPLICATION_H
|
|
||||||
#define FM_APPLICATION_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QtGlobal>
|
|
||||||
#include <QTranslator>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
struct LibFmQtData;
|
|
||||||
|
|
||||||
class LIBFM_QT_API LibFmQt {
|
|
||||||
public:
|
|
||||||
LibFmQt();
|
|
||||||
~LibFmQt();
|
|
||||||
|
|
||||||
QTranslator* translator();
|
|
||||||
|
|
||||||
private:
|
|
||||||
LibFmQt(LibFmQt& other); // disable copy
|
|
||||||
LibFmQtData* d;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_APPLICATION_H
|
|
@ -1,30 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (C) 2012 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 _LIBFM_QT_GLOBALS_
|
|
||||||
#define _LIBFM_QT_GLOBALS_
|
|
||||||
|
|
||||||
#include <QtGlobal>
|
|
||||||
|
|
||||||
#ifdef LIBFM_QT_COMPILATION
|
|
||||||
#define LIBFM_QT_API Q_DECL_EXPORT
|
|
||||||
#else
|
|
||||||
#define LIBFM_QT_API Q_DECL_IMPORT
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,205 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>MountOperationPasswordDialog</class>
|
|
||||||
<widget class="QDialog" name="MountOperationPasswordDialog">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>244</width>
|
|
||||||
<height>302</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Mount</string>
|
|
||||||
</property>
|
|
||||||
<property name="windowIcon">
|
|
||||||
<iconset theme="dialog-password"/>
|
|
||||||
</property>
|
|
||||||
<property name="sizeGripEnabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="modal">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="message">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string/>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QRadioButton" name="Anonymous">
|
|
||||||
<property name="text">
|
|
||||||
<string>Connect &anonymously</string>
|
|
||||||
</property>
|
|
||||||
<attribute name="buttonGroup">
|
|
||||||
<string notr="true">usernameGroup</string>
|
|
||||||
</attribute>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QRadioButton" name="asUser">
|
|
||||||
<property name="text">
|
|
||||||
<string>Connect as u&ser:</string>
|
|
||||||
</property>
|
|
||||||
<attribute name="buttonGroup">
|
|
||||||
<string notr="true">usernameGroup</string>
|
|
||||||
</attribute>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QLineEdit" name="username"/>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>&Username:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>username</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="1">
|
|
||||||
<widget class="QLineEdit" name="password">
|
|
||||||
<property name="echoMode">
|
|
||||||
<enum>QLineEdit::Password</enum>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="3" column="0">
|
|
||||||
<widget class="QLabel" name="label_2">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>&Password:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>password</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="domainLabel">
|
|
||||||
<property name="text">
|
|
||||||
<string>&Domain:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>domain</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="QLineEdit" name="domain"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QRadioButton" name="forgetPassword">
|
|
||||||
<property name="text">
|
|
||||||
<string>Forget password &immediately</string>
|
|
||||||
</property>
|
|
||||||
<attribute name="buttonGroup">
|
|
||||||
<string notr="true">passwordGroup</string>
|
|
||||||
</attribute>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QRadioButton" name="sessionPassword">
|
|
||||||
<property name="text">
|
|
||||||
<string>Remember password until you &logout</string>
|
|
||||||
</property>
|
|
||||||
<attribute name="buttonGroup">
|
|
||||||
<string notr="true">passwordGroup</string>
|
|
||||||
</attribute>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QRadioButton" name="storePassword">
|
|
||||||
<property name="text">
|
|
||||||
<string>Remember &forever</string>
|
|
||||||
</property>
|
|
||||||
<attribute name="buttonGroup">
|
|
||||||
<string notr="true">passwordGroup</string>
|
|
||||||
</attribute>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="standardButtons">
|
|
||||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections>
|
|
||||||
<connection>
|
|
||||||
<sender>buttonBox</sender>
|
|
||||||
<signal>accepted()</signal>
|
|
||||||
<receiver>MountOperationPasswordDialog</receiver>
|
|
||||||
<slot>accept()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>248</x>
|
|
||||||
<y>254</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>157</x>
|
|
||||||
<y>274</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
<connection>
|
|
||||||
<sender>buttonBox</sender>
|
|
||||||
<signal>rejected()</signal>
|
|
||||||
<receiver>MountOperationPasswordDialog</receiver>
|
|
||||||
<slot>reject()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>316</x>
|
|
||||||
<y>260</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>286</x>
|
|
||||||
<y>274</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
</connections>
|
|
||||||
<buttongroups>
|
|
||||||
<buttongroup name="usernameGroup"/>
|
|
||||||
<buttongroup name="passwordGroup"/>
|
|
||||||
</buttongroups>
|
|
||||||
</ui>
|
|
@ -1,227 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 - 2014 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "mountoperation.h"
|
|
||||||
#include <glib/gi18n.h> // for _()
|
|
||||||
#include <QMessageBox>
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QEventLoop>
|
|
||||||
#include "mountoperationpassworddialog_p.h"
|
|
||||||
#include "mountoperationquestiondialog_p.h"
|
|
||||||
#include "ui_mount-operation-password.h"
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
MountOperation::MountOperation(bool interactive, QWidget* parent):
|
|
||||||
QObject(parent),
|
|
||||||
interactive_(interactive),
|
|
||||||
running(false),
|
|
||||||
op(g_mount_operation_new()),
|
|
||||||
cancellable_(g_cancellable_new()),
|
|
||||||
eventLoop(NULL),
|
|
||||||
autoDestroy_(true) {
|
|
||||||
|
|
||||||
g_signal_connect(op, "ask-password", G_CALLBACK(onAskPassword), this);
|
|
||||||
g_signal_connect(op, "ask-question", G_CALLBACK(onAskQuestion), this);
|
|
||||||
// g_signal_connect(op, "reply", G_CALLBACK(onReply), this);
|
|
||||||
|
|
||||||
#if GLIB_CHECK_VERSION(2, 20, 0)
|
|
||||||
g_signal_connect(op, "aborted", G_CALLBACK(onAbort), this);
|
|
||||||
#endif
|
|
||||||
#if GLIB_CHECK_VERSION(2, 22, 0)
|
|
||||||
g_signal_connect(op, "show-processes", G_CALLBACK(onShowProcesses), this);
|
|
||||||
#endif
|
|
||||||
#if GLIB_CHECK_VERSION(2, 34, 0)
|
|
||||||
g_signal_connect(op, "show-unmount-progress", G_CALLBACK(onShowUnmountProgress), this);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
MountOperation::~MountOperation() {
|
|
||||||
qDebug("delete MountOperation");
|
|
||||||
if(cancellable_) {
|
|
||||||
cancel();
|
|
||||||
g_object_unref(cancellable_);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(eventLoop) { // if wait() is called to block the main loop, but the event loop is still running
|
|
||||||
// NOTE: is this possible?
|
|
||||||
eventLoop->exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(op) {
|
|
||||||
g_signal_handlers_disconnect_by_func(op, (gpointer)G_CALLBACK(onAskPassword), this);
|
|
||||||
g_signal_handlers_disconnect_by_func(op, (gpointer)G_CALLBACK(onAskQuestion), this);
|
|
||||||
#if GLIB_CHECK_VERSION(2, 20, 0)
|
|
||||||
g_signal_handlers_disconnect_by_func(op, (gpointer)G_CALLBACK(onAbort), this);
|
|
||||||
#endif
|
|
||||||
#if GLIB_CHECK_VERSION(2, 22, 0)
|
|
||||||
g_signal_handlers_disconnect_by_func(op, (gpointer)G_CALLBACK(onShowProcesses), this);
|
|
||||||
#endif
|
|
||||||
#if GLIB_CHECK_VERSION(2, 34, 0)
|
|
||||||
g_signal_handlers_disconnect_by_func(op, (gpointer)G_CALLBACK(onShowUnmountProgress), this);
|
|
||||||
#endif
|
|
||||||
g_object_unref(op);
|
|
||||||
}
|
|
||||||
// qDebug("MountOperation deleted");
|
|
||||||
}
|
|
||||||
|
|
||||||
void MountOperation::onAbort(GMountOperation* _op, MountOperation* pThis) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void MountOperation::onAskPassword(GMountOperation* _op, gchar* message, gchar* default_user, gchar* default_domain, GAskPasswordFlags flags, MountOperation* pThis) {
|
|
||||||
qDebug("ask password");
|
|
||||||
MountOperationPasswordDialog dlg(pThis, flags);
|
|
||||||
dlg.setMessage(QString::fromUtf8(message));
|
|
||||||
dlg.setDefaultUser(QString::fromUtf8(default_user));
|
|
||||||
dlg.setDefaultDomain(QString::fromUtf8(default_domain));
|
|
||||||
dlg.exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MountOperation::onAskQuestion(GMountOperation* _op, gchar* message, GStrv choices, MountOperation* pThis) {
|
|
||||||
qDebug("ask question");
|
|
||||||
MountOperationQuestionDialog dialog(pThis, message, choices);
|
|
||||||
dialog.exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
void MountOperation::onReply(GMountOperation* _op, GMountOperationResult result, MountOperation* pThis) {
|
|
||||||
qDebug("reply");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
void MountOperation::onShowProcesses(GMountOperation* _op, gchar* message, GArray* processes, GStrv choices, MountOperation* pThis) {
|
|
||||||
qDebug("show processes");
|
|
||||||
}
|
|
||||||
|
|
||||||
void MountOperation::onShowUnmountProgress(GMountOperation* _op, gchar* message, gint64 time_left, gint64 bytes_left, MountOperation* pThis) {
|
|
||||||
qDebug("show unmount progress");
|
|
||||||
}
|
|
||||||
|
|
||||||
void MountOperation::onEjectMountFinished(GMount* mount, GAsyncResult* res, QPointer< MountOperation >* pThis) {
|
|
||||||
if(*pThis) {
|
|
||||||
GError* error = NULL;
|
|
||||||
g_mount_eject_with_operation_finish(mount, res, &error);
|
|
||||||
(*pThis)->handleFinish(error);
|
|
||||||
}
|
|
||||||
delete pThis;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MountOperation::onEjectVolumeFinished(GVolume* volume, GAsyncResult* res, QPointer< MountOperation >* pThis) {
|
|
||||||
if(*pThis) {
|
|
||||||
GError* error = NULL;
|
|
||||||
g_volume_eject_with_operation_finish(volume, res, &error);
|
|
||||||
(*pThis)->handleFinish(error);
|
|
||||||
}
|
|
||||||
delete pThis;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MountOperation::onMountFileFinished(GFile* file, GAsyncResult* res, QPointer< MountOperation >* pThis) {
|
|
||||||
if(*pThis) {
|
|
||||||
GError* error = NULL;
|
|
||||||
g_file_mount_enclosing_volume_finish(file, res, &error);
|
|
||||||
(*pThis)->handleFinish(error);
|
|
||||||
}
|
|
||||||
delete pThis;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MountOperation::onMountVolumeFinished(GVolume* volume, GAsyncResult* res, QPointer< MountOperation >* pThis) {
|
|
||||||
if(*pThis) {
|
|
||||||
GError* error = NULL;
|
|
||||||
g_volume_mount_finish(volume, res, &error);
|
|
||||||
(*pThis)->handleFinish(error);
|
|
||||||
}
|
|
||||||
delete pThis;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MountOperation::onUnmountMountFinished(GMount* mount, GAsyncResult* res, QPointer< MountOperation >* pThis) {
|
|
||||||
if(*pThis) {
|
|
||||||
GError* error = NULL;
|
|
||||||
g_mount_unmount_with_operation_finish(mount, res, &error);
|
|
||||||
(*pThis)->handleFinish(error);
|
|
||||||
}
|
|
||||||
delete pThis;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MountOperation::handleFinish(GError* error) {
|
|
||||||
qDebug("operation finished: %p", error);
|
|
||||||
if(error) {
|
|
||||||
bool showError = interactive_;
|
|
||||||
if(error->domain == G_IO_ERROR) {
|
|
||||||
if(error->code == G_IO_ERROR_FAILED) {
|
|
||||||
// Generate a more human-readable error message instead of using a gvfs one.
|
|
||||||
// The original error message is something like:
|
|
||||||
// Error unmounting: umount exited with exit code 1:
|
|
||||||
// helper failed with: umount: only root can unmount
|
|
||||||
// UUID=18cbf00c-e65f-445a-bccc-11964bdea05d from /media/sda4 */
|
|
||||||
// Why they pass this back to us? This is not human-readable for the users at all.
|
|
||||||
if(strstr(error->message, "only root can ")) {
|
|
||||||
g_free(error->message);
|
|
||||||
error->message = g_strdup(_("Only system administrators have the permission to do this."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(error->code == G_IO_ERROR_FAILED_HANDLED)
|
|
||||||
showError = false;
|
|
||||||
}
|
|
||||||
if(showError)
|
|
||||||
QMessageBox::critical(NULL, QObject::tr("Error"), QString::fromUtf8(error->message));
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_EMIT finished(error);
|
|
||||||
|
|
||||||
if(eventLoop) { // if wait() is called to block the main loop
|
|
||||||
eventLoop->exit(error != NULL ? 1 : 0);
|
|
||||||
eventLoop = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(error)
|
|
||||||
g_error_free(error);
|
|
||||||
|
|
||||||
// free ourself here!!
|
|
||||||
if(autoDestroy_)
|
|
||||||
deleteLater();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MountOperation::prepareUnmount(GMount* mount) {
|
|
||||||
/* ensure that CWD is not on the mounted filesystem. */
|
|
||||||
char* cwd_str = g_get_current_dir();
|
|
||||||
GFile* cwd = g_file_new_for_path(cwd_str);
|
|
||||||
GFile* root = g_mount_get_root(mount);
|
|
||||||
g_free(cwd_str);
|
|
||||||
/* FIXME: This cannot cover 100% cases since symlinks are not checked.
|
|
||||||
* There may be other cases that cwd is actually under mount root
|
|
||||||
* but checking prefix is not enough. We already did our best, though. */
|
|
||||||
if(g_file_has_prefix(cwd, root))
|
|
||||||
g_chdir("/");
|
|
||||||
g_object_unref(cwd);
|
|
||||||
g_object_unref(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
// block the operation used an internal QEventLoop and returns
|
|
||||||
// only after the whole operation is finished.
|
|
||||||
bool MountOperation::wait() {
|
|
||||||
QEventLoop loop;
|
|
||||||
eventLoop = &loop;
|
|
||||||
int exitCode = loop.exec();
|
|
||||||
return exitCode == 0 ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Fm
|
|
@ -1,155 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef FM_MOUNTOPERATION_H
|
|
||||||
#define FM_MOUNTOPERATION_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QWidget>
|
|
||||||
#include <QDialog>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
#include <gio/gio.h>
|
|
||||||
#include <QPointer>
|
|
||||||
|
|
||||||
class QEventLoop;
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
// FIXME: the original APIs in gtk+ version of libfm for mounting devices is poor.
|
|
||||||
// Need to find a better API design which make things fully async and cancellable.
|
|
||||||
|
|
||||||
// FIXME: parent_ does not work. All dialogs shown by the mount operation has no parent window assigned.
|
|
||||||
// FIXME: Need to reconsider the propery way of API design. Blocking sync calls are handy, but
|
|
||||||
// indeed causes some problems. :-(
|
|
||||||
|
|
||||||
class LIBFM_QT_API MountOperation: public QObject {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit MountOperation(bool interactive = true, QWidget* parent = 0);
|
|
||||||
~MountOperation();
|
|
||||||
|
|
||||||
void mount(FmPath* path) {
|
|
||||||
GFile* gf = fm_path_to_gfile(path);
|
|
||||||
g_file_mount_enclosing_volume(gf, G_MOUNT_MOUNT_NONE, op, cancellable_, (GAsyncReadyCallback)onMountFileFinished, new QPointer<MountOperation>(this));
|
|
||||||
g_object_unref(gf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void mount(GVolume* volume) {
|
|
||||||
g_volume_mount(volume, G_MOUNT_MOUNT_NONE, op, cancellable_, (GAsyncReadyCallback)onMountVolumeFinished, new QPointer<MountOperation>(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
void unmount(GMount* mount) {
|
|
||||||
prepareUnmount(mount);
|
|
||||||
g_mount_unmount_with_operation(mount, G_MOUNT_UNMOUNT_NONE, op, cancellable_, (GAsyncReadyCallback)onUnmountMountFinished, new QPointer<MountOperation>(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
void unmount(GVolume* volume) {
|
|
||||||
GMount* mount = g_volume_get_mount(volume);
|
|
||||||
if(!mount)
|
|
||||||
return;
|
|
||||||
unmount(mount);
|
|
||||||
g_object_unref(mount);
|
|
||||||
}
|
|
||||||
|
|
||||||
void eject(GMount* mount) {
|
|
||||||
prepareUnmount(mount);
|
|
||||||
g_mount_eject_with_operation(mount, G_MOUNT_UNMOUNT_NONE, op, cancellable_, (GAsyncReadyCallback)onEjectMountFinished, new QPointer<MountOperation>(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
void eject(GVolume* volume) {
|
|
||||||
GMount* mnt = g_volume_get_mount(volume);
|
|
||||||
prepareUnmount(mnt);
|
|
||||||
g_object_unref(mnt);
|
|
||||||
g_volume_eject_with_operation(volume, G_MOUNT_UNMOUNT_NONE, op, cancellable_, (GAsyncReadyCallback)onEjectVolumeFinished, new QPointer<MountOperation>(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
QWidget* parent() const {
|
|
||||||
return parent_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setParent(QWidget* parent) {
|
|
||||||
parent_ = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
GCancellable* cancellable() const {
|
|
||||||
return cancellable_;
|
|
||||||
}
|
|
||||||
|
|
||||||
GMountOperation* mountOperation() {
|
|
||||||
return op;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cancel() {
|
|
||||||
g_cancellable_cancel(cancellable_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isRunning() const {
|
|
||||||
return running;
|
|
||||||
}
|
|
||||||
|
|
||||||
// block the operation used an internal QEventLoop and returns
|
|
||||||
// only after the whole operation is finished.
|
|
||||||
bool wait();
|
|
||||||
|
|
||||||
bool autoDestroy() {
|
|
||||||
return autoDestroy_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setAutoDestroy(bool destroy = true) {
|
|
||||||
autoDestroy_ = destroy;
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void finished(GError* error = NULL);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void prepareUnmount(GMount* mount);
|
|
||||||
|
|
||||||
static void onAskPassword(GMountOperation *_op, gchar* message, gchar* default_user, gchar* default_domain, GAskPasswordFlags flags, MountOperation* pThis);
|
|
||||||
static void onAskQuestion(GMountOperation *_op, gchar* message, GStrv choices, MountOperation* pThis);
|
|
||||||
// static void onReply(GMountOperation *_op, GMountOperationResult result, MountOperation* pThis);
|
|
||||||
|
|
||||||
static void onAbort(GMountOperation *_op, MountOperation* pThis);
|
|
||||||
static void onShowProcesses(GMountOperation *_op, gchar* message, GArray* processes, GStrv choices, MountOperation* pThis);
|
|
||||||
static void onShowUnmountProgress(GMountOperation *_op, gchar* message, gint64 time_left, gint64 bytes_left, MountOperation* pThis);
|
|
||||||
|
|
||||||
// it's possible that this object is freed when the callback is called by gio, so guarding with QPointer is needed here.
|
|
||||||
static void onMountFileFinished(GFile* file, GAsyncResult *res, QPointer<MountOperation>* pThis);
|
|
||||||
static void onMountVolumeFinished(GVolume* volume, GAsyncResult *res, QPointer<MountOperation>* pThis);
|
|
||||||
static void onUnmountMountFinished(GMount* mount, GAsyncResult *res, QPointer<MountOperation>* pThis);
|
|
||||||
static void onEjectMountFinished(GMount* mount, GAsyncResult *res, QPointer<MountOperation>* pThis);
|
|
||||||
static void onEjectVolumeFinished(GVolume* volume, GAsyncResult *res, QPointer<MountOperation>* pThis);
|
|
||||||
|
|
||||||
void handleFinish(GError* error);
|
|
||||||
|
|
||||||
private:
|
|
||||||
GMountOperation* op;
|
|
||||||
GCancellable* cancellable_;
|
|
||||||
QWidget* parent_;
|
|
||||||
bool running;
|
|
||||||
bool interactive_;
|
|
||||||
QEventLoop* eventLoop;
|
|
||||||
bool autoDestroy_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_MOUNTOPERATION_H
|
|
@ -1,125 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "mountoperationpassworddialog_p.h"
|
|
||||||
#include "ui_mount-operation-password.h"
|
|
||||||
#include "mountoperation.h"
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
MountOperationPasswordDialog::MountOperationPasswordDialog(MountOperation* op, GAskPasswordFlags flags):
|
|
||||||
QDialog(),
|
|
||||||
mountOperation(op),
|
|
||||||
canAnonymous(flags & G_ASK_PASSWORD_ANONYMOUS_SUPPORTED ? true : false),
|
|
||||||
canSavePassword(flags & G_ASK_PASSWORD_SAVING_SUPPORTED ? true : false),
|
|
||||||
needUserName(flags & G_ASK_PASSWORD_NEED_USERNAME ? true : false),
|
|
||||||
needPassword(flags & G_ASK_PASSWORD_NEED_PASSWORD ? true : false),
|
|
||||||
needDomain(flags & G_ASK_PASSWORD_NEED_DOMAIN ? true : false) {
|
|
||||||
|
|
||||||
ui = new Ui::MountOperationPasswordDialog();
|
|
||||||
ui->setupUi(this);
|
|
||||||
|
|
||||||
// change the text of Ok button to Connect
|
|
||||||
ui->buttonBox->buttons().first()->setText(tr("&Connect"));
|
|
||||||
connect(ui->Anonymous, &QAbstractButton::toggled, this, &MountOperationPasswordDialog::onAnonymousToggled);
|
|
||||||
|
|
||||||
if(canAnonymous) {
|
|
||||||
// select ananymous by default if applicable.
|
|
||||||
ui->Anonymous->setChecked(true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ui->Anonymous->setEnabled(false);
|
|
||||||
}
|
|
||||||
if(!needUserName) {
|
|
||||||
ui->username->setEnabled(false);
|
|
||||||
}
|
|
||||||
if(!needPassword) {
|
|
||||||
ui->password->setEnabled(false);
|
|
||||||
}
|
|
||||||
if(!needDomain) {
|
|
||||||
ui->domain->hide();
|
|
||||||
ui->domainLabel->hide();
|
|
||||||
}
|
|
||||||
if(canSavePassword) {
|
|
||||||
ui->sessionPassword->setChecked(true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ui->storePassword->setEnabled(false);
|
|
||||||
ui->sessionPassword->setEnabled(false);
|
|
||||||
ui->forgetPassword->setChecked(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MountOperationPasswordDialog::~MountOperationPasswordDialog() {
|
|
||||||
delete ui;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MountOperationPasswordDialog::onAnonymousToggled(bool checked) {
|
|
||||||
// disable username/password entries if anonymous mode is used
|
|
||||||
bool useUserPassword = !checked;
|
|
||||||
if(needUserName)
|
|
||||||
ui->username->setEnabled(useUserPassword);
|
|
||||||
if(needPassword)
|
|
||||||
ui->password->setEnabled(useUserPassword);
|
|
||||||
if(needDomain)
|
|
||||||
ui->domain->setEnabled(useUserPassword);
|
|
||||||
|
|
||||||
if(canSavePassword) {
|
|
||||||
ui->forgetPassword->setEnabled(useUserPassword);
|
|
||||||
ui->sessionPassword->setEnabled(useUserPassword);
|
|
||||||
ui->storePassword->setEnabled(useUserPassword);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MountOperationPasswordDialog::setMessage(QString message) {
|
|
||||||
ui->message->setText(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MountOperationPasswordDialog::setDefaultDomain(QString domain) {
|
|
||||||
ui->domain->setText(domain);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MountOperationPasswordDialog::setDefaultUser(QString user) {
|
|
||||||
ui->username->setText(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MountOperationPasswordDialog::done(int r) {
|
|
||||||
GMountOperation* gmop = mountOperation->mountOperation();
|
|
||||||
|
|
||||||
if(r == QDialog::Accepted) {
|
|
||||||
|
|
||||||
if(needUserName)
|
|
||||||
g_mount_operation_set_username(gmop, ui->username->text().toUtf8());
|
|
||||||
if(needDomain)
|
|
||||||
g_mount_operation_set_domain(gmop, ui->domain->text().toUtf8());
|
|
||||||
if(needPassword)
|
|
||||||
g_mount_operation_set_password(gmop, ui->password->text().toUtf8());
|
|
||||||
if(canAnonymous)
|
|
||||||
g_mount_operation_set_anonymous(gmop, ui->Anonymous->isChecked());
|
|
||||||
|
|
||||||
g_mount_operation_reply(gmop, G_MOUNT_OPERATION_HANDLED);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
g_mount_operation_reply(gmop, G_MOUNT_OPERATION_ABORTED);
|
|
||||||
}
|
|
||||||
QDialog::done(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Fm
|
|
@ -1,64 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef FM_MOUNTOPERATIONPASSWORDDIALOG_H
|
|
||||||
#define FM_MOUNTOPERATIONPASSWORDDIALOG_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QDialog>
|
|
||||||
#include <gio/gio.h>
|
|
||||||
|
|
||||||
namespace Ui {
|
|
||||||
class MountOperationPasswordDialog;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class MountOperation;
|
|
||||||
|
|
||||||
class MountOperationPasswordDialog : public QDialog {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit MountOperationPasswordDialog(MountOperation* op, GAskPasswordFlags flags);
|
|
||||||
virtual ~MountOperationPasswordDialog();
|
|
||||||
|
|
||||||
void setMessage(QString message);
|
|
||||||
void setDefaultUser(QString user);
|
|
||||||
void setDefaultDomain(QString domain);
|
|
||||||
|
|
||||||
virtual void done(int r);
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void onAnonymousToggled(bool checked);
|
|
||||||
|
|
||||||
private:
|
|
||||||
Ui::MountOperationPasswordDialog* ui;
|
|
||||||
MountOperation* mountOperation;
|
|
||||||
bool needPassword;
|
|
||||||
bool needUserName;
|
|
||||||
bool needDomain;
|
|
||||||
bool canSavePassword;
|
|
||||||
bool canAnonymous;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_MOUNTOPERATIONPASSWORDDIALOG_H
|
|
@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "mountoperationquestiondialog_p.h"
|
|
||||||
#include "mountoperation.h"
|
|
||||||
#include <QPushButton>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
MountOperationQuestionDialog::MountOperationQuestionDialog(MountOperation* op, gchar* message, GStrv choices):
|
|
||||||
QMessageBox(),
|
|
||||||
mountOperation(op) {
|
|
||||||
|
|
||||||
setIcon(QMessageBox::Question);
|
|
||||||
setText(QString::fromUtf8(message));
|
|
||||||
|
|
||||||
choiceCount = g_strv_length(choices);
|
|
||||||
choiceButtons = new QAbstractButton*[choiceCount];
|
|
||||||
for(int i = 0; i < choiceCount; ++i) {
|
|
||||||
// It's not allowed to add custom buttons without standard roles
|
|
||||||
// to QMessageBox. So we set role of all buttons to AcceptRole and
|
|
||||||
// handle their clicked() signals in our own slots.
|
|
||||||
// When anyone of the buttons is clicked, exec() always returns "accept".
|
|
||||||
QPushButton* button = new QPushButton(QString::fromUtf8(choices[i]));
|
|
||||||
addButton(button, QMessageBox::AcceptRole);
|
|
||||||
choiceButtons[i] = button;
|
|
||||||
}
|
|
||||||
connect(this, &MountOperationQuestionDialog::buttonClicked, this, &MountOperationQuestionDialog::onButtonClicked);
|
|
||||||
}
|
|
||||||
|
|
||||||
MountOperationQuestionDialog::~MountOperationQuestionDialog() {
|
|
||||||
delete []choiceButtons;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MountOperationQuestionDialog::done(int r) {
|
|
||||||
if(r != QDialog::Accepted) {
|
|
||||||
GMountOperation* op = mountOperation->mountOperation();
|
|
||||||
g_mount_operation_reply(op, G_MOUNT_OPERATION_ABORTED);
|
|
||||||
}
|
|
||||||
QDialog::done(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MountOperationQuestionDialog::onButtonClicked(QAbstractButton* button) {
|
|
||||||
GMountOperation* op = mountOperation->mountOperation();
|
|
||||||
for(int i = 0; i < choiceCount; ++i) {
|
|
||||||
if(choiceButtons[i] == button) {
|
|
||||||
g_mount_operation_set_choice(op, i);
|
|
||||||
g_mount_operation_reply(op, G_MOUNT_OPERATION_HANDLED);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Fm
|
|
@ -1,51 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef FM_MOUNTOPERATIONQUESTIONDIALOG_H
|
|
||||||
#define FM_MOUNTOPERATIONQUESTIONDIALOG_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QMessageBox>
|
|
||||||
#include <gio/gio.h>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class MountOperation;
|
|
||||||
|
|
||||||
class MountOperationQuestionDialog : public QMessageBox {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
MountOperationQuestionDialog(MountOperation* op, gchar* message, GStrv choices);
|
|
||||||
virtual ~MountOperationQuestionDialog();
|
|
||||||
|
|
||||||
virtual void done(int r);
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void onButtonClicked(QAbstractButton* button);
|
|
||||||
|
|
||||||
private:
|
|
||||||
MountOperation* mountOperation;
|
|
||||||
QAbstractButton** choiceButtons;
|
|
||||||
int choiceCount;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_MOUNTOPERATIONQUESTIONDIALOG_H
|
|
@ -1,22 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2014 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "path.h"
|
|
||||||
|
|
||||||
using namespace Fm;
|
|
@ -1,247 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2014 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FM_PATH_H
|
|
||||||
#define FM_PATH_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
#include <QString>
|
|
||||||
#include <QMetaType>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class LIBFM_QT_API Path {
|
|
||||||
public:
|
|
||||||
|
|
||||||
Path(): data_(NULL) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Path(FmPath* path, bool takeOwnership = false): data_(path) {
|
|
||||||
if(path && !takeOwnership)
|
|
||||||
fm_path_ref(data_);
|
|
||||||
}
|
|
||||||
|
|
||||||
Path(const Path& other): data_(other.data_ ? fm_path_ref(other.data_) : NULL) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Path(GFile* gf): data_(fm_path_new_for_gfile(gf)) {
|
|
||||||
}
|
|
||||||
|
|
||||||
~Path() {
|
|
||||||
if(data_)
|
|
||||||
fm_path_unref(data_);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Path fromPathName(const char* path_name) {
|
|
||||||
return Path(fm_path_new_for_path(path_name), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Path fromUri(const char* uri) {
|
|
||||||
return Path(fm_path_new_for_uri(uri), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Path fromDisplayName(const char* path_name) {
|
|
||||||
return Path(fm_path_new_for_display_name(path_name), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Path fromString(const char* path_str) {
|
|
||||||
return Path(fm_path_new_for_str(path_str), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Path fromCommandlineArg(const char* arg) {
|
|
||||||
return Path(fm_path_new_for_commandline_arg(arg), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Path child(const char* basename) {
|
|
||||||
return Path(fm_path_new_child(data_, basename), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Path child(const char* basename, int name_len) {
|
|
||||||
return Path(fm_path_new_child_len(data_, basename, name_len), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Path relative(const char* rel) {
|
|
||||||
return Path(fm_path_new_relative(data_, rel), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* predefined paths */
|
|
||||||
static Path root(void) { /* / */
|
|
||||||
return Path(fm_path_get_root(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Path home(void) { /* home directory */
|
|
||||||
return Path(fm_path_get_home(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Path desktop(void) { /* $HOME/Desktop */
|
|
||||||
return Path(fm_path_get_desktop(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Path trash(void) { /* trash:/// */
|
|
||||||
return Path(fm_path_get_trash(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Path appsMenu(void) { /* menu://applications.menu/ */
|
|
||||||
return Path(fm_path_get_apps_menu(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Path parent() {
|
|
||||||
return Path(fm_path_get_parent(data_), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* basename() {
|
|
||||||
return fm_path_get_basename(data_);
|
|
||||||
}
|
|
||||||
|
|
||||||
FmPathFlags flags() {
|
|
||||||
return fm_path_get_flags(data_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasPrefix(FmPath* prefix) {
|
|
||||||
return fm_path_has_prefix(data_, prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
Path schemePath() {
|
|
||||||
return Path(fm_path_get_scheme_path(data_), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isNative() {
|
|
||||||
return fm_path_is_native(data_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isTrash() {
|
|
||||||
return fm_path_is_trash(data_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isTrashRoot() {
|
|
||||||
return fm_path_is_trash_root(data_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isNativeOrTrash() {
|
|
||||||
return fm_path_is_native_or_trash(data_);
|
|
||||||
}
|
|
||||||
|
|
||||||
char* toString() {
|
|
||||||
return fm_path_to_str(data_);
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray toByteArray() {
|
|
||||||
char* s = fm_path_to_str(data_);
|
|
||||||
QByteArray str(s);
|
|
||||||
g_free(s);
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* toUri() {
|
|
||||||
return fm_path_to_uri(data_);
|
|
||||||
}
|
|
||||||
|
|
||||||
GFile* toGfile() {
|
|
||||||
return fm_path_to_gfile(data_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
char* displayName(bool human_readable = true) {
|
|
||||||
return fm_path_display_name(data_, human_readable);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
QString displayName(bool human_readable = true) {
|
|
||||||
char* dispname = fm_path_display_name(data_, human_readable);
|
|
||||||
QString str = QString::fromUtf8(dispname);
|
|
||||||
g_free(dispname);
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
char* displayBasename() {
|
|
||||||
return fm_path_display_basename(data_);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
QString displayBasename() {
|
|
||||||
char* basename = fm_path_display_basename(data_);
|
|
||||||
QString s = QString::fromUtf8(basename);
|
|
||||||
g_free(basename);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* For used in hash tables */
|
|
||||||
guint hash() {
|
|
||||||
return fm_path_hash(data_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void take(FmPath* path) { // take the ownership of the "path"
|
|
||||||
if(data_)
|
|
||||||
fm_path_unref(data_);
|
|
||||||
data_ = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
Path& operator = (const Path& other) {
|
|
||||||
if(data_)
|
|
||||||
fm_path_unref(data_);
|
|
||||||
data_ = fm_path_ref(other.data_);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator == (const Path& other) const {
|
|
||||||
return fm_path_equal(data_, other.data_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator != (const Path& other) const {
|
|
||||||
return !fm_path_equal(data_, other.data_);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator < (const Path& other) const {
|
|
||||||
return compare(other);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator > (const Path& other) const {
|
|
||||||
return (other < *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* can be used for sorting */
|
|
||||||
int compare(const Path& other) const {
|
|
||||||
return fm_path_compare(data_, other.data_);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* used for completion in entry */
|
|
||||||
bool equal(const gchar *str, int n) const {
|
|
||||||
return fm_path_equal_str(data_, str, n);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* calculate how many elements are in this path. */
|
|
||||||
int depth() const {
|
|
||||||
return fm_path_depth(data_);
|
|
||||||
}
|
|
||||||
|
|
||||||
FmPath* data() const {
|
|
||||||
return data_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
FmPath* data_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Q_DECLARE_OPAQUE_POINTER(FmPath*)
|
|
||||||
|
|
||||||
#endif // FM_PATH_H
|
|
@ -1,195 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "pathedit.h"
|
|
||||||
#include <QCompleter>
|
|
||||||
#include <QStringListModel>
|
|
||||||
#include <QStringBuilder>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
PathEdit::PathEdit(QWidget* parent):
|
|
||||||
QLineEdit(parent),
|
|
||||||
cancellable_(NULL),
|
|
||||||
model_(new QStringListModel()),
|
|
||||||
completer_(new QCompleter()) {
|
|
||||||
setCompleter(completer_);
|
|
||||||
completer_->setModel(model_);
|
|
||||||
connect(this, &PathEdit::textChanged, this, &PathEdit::onTextChanged);
|
|
||||||
}
|
|
||||||
|
|
||||||
PathEdit::~PathEdit() {
|
|
||||||
delete completer_;
|
|
||||||
if(model_)
|
|
||||||
delete model_;
|
|
||||||
if(cancellable_) {
|
|
||||||
g_cancellable_cancel(cancellable_);
|
|
||||||
g_object_unref(cancellable_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PathEdit::focusInEvent(QFocusEvent* e) {
|
|
||||||
QLineEdit::focusInEvent(e);
|
|
||||||
// build the completion list only when we have the keyboard focus
|
|
||||||
reloadCompleter(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PathEdit::focusOutEvent(QFocusEvent* e) {
|
|
||||||
QLineEdit::focusOutEvent(e);
|
|
||||||
// free the completion list since we don't need it anymore
|
|
||||||
freeCompleter();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PathEdit::onTextChanged(const QString& text) {
|
|
||||||
int pos = text.lastIndexOf('/');
|
|
||||||
if(pos >= 0)
|
|
||||||
++pos;
|
|
||||||
else
|
|
||||||
pos = text.length();
|
|
||||||
QString newPrefix = text.left(pos);
|
|
||||||
if(currentPrefix_ != newPrefix) {
|
|
||||||
currentPrefix_ = newPrefix;
|
|
||||||
// only build the completion list if we have the keyboard focus
|
|
||||||
// if we don't have the focus now, then we'll rebuild the completion list
|
|
||||||
// when focusInEvent happens. this avoid unnecessary dir loading.
|
|
||||||
if(hasFocus())
|
|
||||||
reloadCompleter(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct JobData {
|
|
||||||
GCancellable* cancellable;
|
|
||||||
GFile* dirName;
|
|
||||||
QStringList subDirs;
|
|
||||||
PathEdit* edit;
|
|
||||||
bool triggeredByFocusInEvent;
|
|
||||||
|
|
||||||
~JobData() {
|
|
||||||
g_object_unref(dirName);
|
|
||||||
g_object_unref(cancellable);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void freeMe(JobData* data) {
|
|
||||||
delete data;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void PathEdit::reloadCompleter(bool triggeredByFocusInEvent) {
|
|
||||||
// parent dir has been changed, reload dir list
|
|
||||||
// if(currentPrefix_[0] == "~") { // special case for home dir
|
|
||||||
// cancel running dir-listing jobs, if there's any
|
|
||||||
if(cancellable_) {
|
|
||||||
g_cancellable_cancel(cancellable_);
|
|
||||||
g_object_unref(cancellable_);
|
|
||||||
}
|
|
||||||
// launch a new job to do dir listing
|
|
||||||
JobData* data = new JobData();
|
|
||||||
data->edit = this;
|
|
||||||
data->triggeredByFocusInEvent = triggeredByFocusInEvent;
|
|
||||||
// need to use fm_file_new_for_commandline_arg() rather than g_file_new_for_commandline_arg().
|
|
||||||
// otherwise, our own vfs, such as menu://, won't be loaded.
|
|
||||||
data->dirName = fm_file_new_for_commandline_arg(currentPrefix_.toLocal8Bit().constData());
|
|
||||||
// qDebug("load: %s", g_file_get_uri(data->dirName));
|
|
||||||
cancellable_ = g_cancellable_new();
|
|
||||||
data->cancellable = (GCancellable*)g_object_ref(cancellable_);
|
|
||||||
g_io_scheduler_push_job((GIOSchedulerJobFunc)jobFunc,
|
|
||||||
data, (GDestroyNotify)JobData::freeMe,
|
|
||||||
G_PRIORITY_LOW, cancellable_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PathEdit::freeCompleter() {
|
|
||||||
if(cancellable_) {
|
|
||||||
g_cancellable_cancel(cancellable_);
|
|
||||||
g_object_unref(cancellable_);
|
|
||||||
cancellable_ = NULL;
|
|
||||||
}
|
|
||||||
model_->setStringList(QStringList());
|
|
||||||
}
|
|
||||||
|
|
||||||
gboolean PathEdit::jobFunc(GIOSchedulerJob* job, GCancellable* cancellable, gpointer user_data) {
|
|
||||||
JobData* data = reinterpret_cast<JobData*>(user_data);
|
|
||||||
GError *err = NULL;
|
|
||||||
GFileEnumerator* enu = g_file_enumerate_children(data->dirName,
|
|
||||||
// G_FILE_ATTRIBUTE_STANDARD_NAME","
|
|
||||||
G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME","
|
|
||||||
G_FILE_ATTRIBUTE_STANDARD_TYPE,
|
|
||||||
G_FILE_QUERY_INFO_NONE, cancellable,
|
|
||||||
&err);
|
|
||||||
if(enu) {
|
|
||||||
while(!g_cancellable_is_cancelled(cancellable)) {
|
|
||||||
GFileInfo* inf = g_file_enumerator_next_file(enu, cancellable, &err);
|
|
||||||
if(inf) {
|
|
||||||
GFileType type = g_file_info_get_file_type(inf);
|
|
||||||
if(type == G_FILE_TYPE_DIRECTORY) {
|
|
||||||
const char* name = g_file_info_get_display_name(inf);
|
|
||||||
// FIXME: encoding conversion here?
|
|
||||||
data->subDirs.append(QString::fromUtf8(name));
|
|
||||||
}
|
|
||||||
g_object_unref(inf);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(err) {
|
|
||||||
g_error_free(err);
|
|
||||||
err = NULL;
|
|
||||||
}
|
|
||||||
else /* EOF */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
g_file_enumerator_close(enu, cancellable, NULL);
|
|
||||||
g_object_unref(enu);
|
|
||||||
}
|
|
||||||
// finished! let's update the UI in the main thread
|
|
||||||
g_io_scheduler_job_send_to_mainloop(job, _onJobFinished, data, NULL);
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
gboolean PathEdit::_onJobFinished(gpointer user_data) {
|
|
||||||
JobData* data = reinterpret_cast<JobData*>(user_data);
|
|
||||||
data->edit->onJobFinished(data);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This callback function is called from main thread so it's safe to access the GUI
|
|
||||||
void PathEdit::onJobFinished(JobData* data) {
|
|
||||||
if(!g_cancellable_is_cancelled(data->cancellable)) {
|
|
||||||
// update the completer only if the job is not cancelled
|
|
||||||
QStringList::iterator it;
|
|
||||||
for(it = data->subDirs.begin(); it != data->subDirs.end(); ++it) {
|
|
||||||
// qDebug("%s", it->toUtf8().constData());
|
|
||||||
*it = (currentPrefix_ % *it);
|
|
||||||
}
|
|
||||||
model_->setStringList(data->subDirs);
|
|
||||||
// trigger completion manually
|
|
||||||
if(hasFocus() && !data->triggeredByFocusInEvent)
|
|
||||||
completer_->complete();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
model_->setStringList(QStringList());
|
|
||||||
if(cancellable_) {
|
|
||||||
g_object_unref(cancellable_);
|
|
||||||
cancellable_ = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Fm
|
|
@ -1,64 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef FM_PATHEDIT_H
|
|
||||||
#define FM_PATHEDIT_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QLineEdit>
|
|
||||||
#include <gio/gio.h>
|
|
||||||
|
|
||||||
class QCompleter;
|
|
||||||
class QStringListModel;
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
struct JobData;
|
|
||||||
|
|
||||||
class LIBFM_QT_API PathEdit : public QLineEdit {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit PathEdit(QWidget* parent = 0);
|
|
||||||
virtual ~PathEdit();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void focusInEvent(QFocusEvent* e);
|
|
||||||
virtual void focusOutEvent(QFocusEvent* e);
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
|
||||||
void onTextChanged(const QString & text);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void reloadCompleter(bool triggeredByFocusInEvent = false);
|
|
||||||
void freeCompleter();
|
|
||||||
static gboolean jobFunc(GIOSchedulerJob *job, GCancellable *cancellable, gpointer user_data);
|
|
||||||
static gboolean _onJobFinished(gpointer user_data);
|
|
||||||
void onJobFinished(JobData* data);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QCompleter* completer_;
|
|
||||||
QStringListModel* model_;
|
|
||||||
QString currentPrefix_;
|
|
||||||
GCancellable* cancellable_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_PATHEDIT_H
|
|
@ -1,521 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "placesmodel.h"
|
|
||||||
#include "icontheme.h"
|
|
||||||
#include <gio/gio.h>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QMimeData>
|
|
||||||
#include <QTimer>
|
|
||||||
#include "utilities.h"
|
|
||||||
#include "placesmodelitem.h"
|
|
||||||
|
|
||||||
using namespace Fm;
|
|
||||||
|
|
||||||
PlacesModel::PlacesModel(QObject* parent):
|
|
||||||
QStandardItemModel(parent),
|
|
||||||
showApplications_(true),
|
|
||||||
showDesktop_(true),
|
|
||||||
ejectIcon_(QIcon::fromTheme("media-eject")) {
|
|
||||||
|
|
||||||
setColumnCount(2);
|
|
||||||
|
|
||||||
placesRoot = new QStandardItem(tr("Places"));
|
|
||||||
placesRoot->setSelectable(false);
|
|
||||||
placesRoot->setColumnCount(2);
|
|
||||||
appendRow(placesRoot);
|
|
||||||
|
|
||||||
homeItem = new PlacesModelItem("user-home", g_get_user_name(), fm_path_get_home());
|
|
||||||
placesRoot->appendRow(homeItem);
|
|
||||||
|
|
||||||
desktopItem = new PlacesModelItem("user-desktop", tr("Desktop"), fm_path_get_desktop());
|
|
||||||
placesRoot->appendRow(desktopItem);
|
|
||||||
|
|
||||||
createTrashItem();
|
|
||||||
|
|
||||||
FmPath* path;
|
|
||||||
// FIXME: add an option to hide network:///
|
|
||||||
if(true) {
|
|
||||||
path = fm_path_new_for_uri("computer:///");
|
|
||||||
computerItem = new PlacesModelItem("computer", tr("Computer"), path);
|
|
||||||
fm_path_unref(path);
|
|
||||||
placesRoot->appendRow(computerItem);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
computerItem = NULL;
|
|
||||||
|
|
||||||
// FIXME: add an option to hide applications:///
|
|
||||||
const char* applicaion_icon_names[] = {"system-software-install", "applications-accessories", "application-x-executable"};
|
|
||||||
// NOTE: g_themed_icon_new_from_names() accepts char**, but actually const char** is OK.
|
|
||||||
GIcon* gicon = g_themed_icon_new_from_names((char**)applicaion_icon_names, G_N_ELEMENTS(applicaion_icon_names));
|
|
||||||
FmIcon* fmicon = fm_icon_from_gicon(gicon);
|
|
||||||
g_object_unref(gicon);
|
|
||||||
applicationsItem = new PlacesModelItem(fmicon, tr("Applications"), fm_path_get_apps_menu());
|
|
||||||
fm_icon_unref(fmicon);
|
|
||||||
placesRoot->appendRow(applicationsItem);
|
|
||||||
|
|
||||||
// FIXME: add an option to hide network:///
|
|
||||||
if(true) {
|
|
||||||
const char* network_icon_names[] = {"network", "folder-network", "folder"};
|
|
||||||
// NOTE: g_themed_icon_new_from_names() accepts char**, but actually const char** is OK.
|
|
||||||
gicon = g_themed_icon_new_from_names((char**)network_icon_names, G_N_ELEMENTS(network_icon_names));
|
|
||||||
fmicon = fm_icon_from_gicon(gicon);
|
|
||||||
g_object_unref(gicon);
|
|
||||||
path = fm_path_new_for_uri("network:///");
|
|
||||||
networkItem = new PlacesModelItem(fmicon, tr("Network"), path);
|
|
||||||
fm_icon_unref(fmicon);
|
|
||||||
fm_path_unref(path);
|
|
||||||
placesRoot->appendRow(networkItem);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
networkItem = NULL;
|
|
||||||
|
|
||||||
devicesRoot = new QStandardItem(tr("Devices"));
|
|
||||||
devicesRoot->setSelectable(false);
|
|
||||||
devicesRoot->setColumnCount(2);
|
|
||||||
appendRow(devicesRoot);
|
|
||||||
|
|
||||||
// volumes
|
|
||||||
volumeMonitor = g_volume_monitor_get();
|
|
||||||
if(volumeMonitor) {
|
|
||||||
g_signal_connect(volumeMonitor, "volume-added", G_CALLBACK(onVolumeAdded), this);
|
|
||||||
g_signal_connect(volumeMonitor, "volume-removed", G_CALLBACK(onVolumeRemoved), this);
|
|
||||||
g_signal_connect(volumeMonitor, "volume-changed", G_CALLBACK(onVolumeChanged), this);
|
|
||||||
g_signal_connect(volumeMonitor, "mount-added", G_CALLBACK(onMountAdded), this);
|
|
||||||
g_signal_connect(volumeMonitor, "mount-changed", G_CALLBACK(onMountChanged), this);
|
|
||||||
g_signal_connect(volumeMonitor, "mount-removed", G_CALLBACK(onMountRemoved), this);
|
|
||||||
|
|
||||||
// add volumes to side-pane
|
|
||||||
GList* vols = g_volume_monitor_get_volumes(volumeMonitor);
|
|
||||||
GList* l;
|
|
||||||
for(l = vols; l; l = l->next) {
|
|
||||||
GVolume* volume = G_VOLUME(l->data);
|
|
||||||
onVolumeAdded(volumeMonitor, volume, this);
|
|
||||||
g_object_unref(volume);
|
|
||||||
}
|
|
||||||
g_list_free(vols);
|
|
||||||
|
|
||||||
/* add mounts to side-pane */
|
|
||||||
vols = g_volume_monitor_get_mounts(volumeMonitor);
|
|
||||||
for(l = vols; l; l = l->next) {
|
|
||||||
GMount* mount = G_MOUNT(l->data);
|
|
||||||
GVolume* volume = g_mount_get_volume(mount);
|
|
||||||
if(volume)
|
|
||||||
g_object_unref(volume);
|
|
||||||
else { /* network mounts or others */
|
|
||||||
PlacesModelItem* item = new PlacesModelMountItem(mount);
|
|
||||||
devicesRoot->appendRow(item);
|
|
||||||
}
|
|
||||||
g_object_unref(mount);
|
|
||||||
}
|
|
||||||
g_list_free(vols);
|
|
||||||
}
|
|
||||||
|
|
||||||
// bookmarks
|
|
||||||
bookmarksRoot = new QStandardItem(tr("Bookmarks"));
|
|
||||||
bookmarksRoot->setSelectable(false);
|
|
||||||
bookmarksRoot->setColumnCount(2);
|
|
||||||
appendRow(bookmarksRoot);
|
|
||||||
|
|
||||||
bookmarks = fm_bookmarks_dup();
|
|
||||||
loadBookmarks();
|
|
||||||
g_signal_connect(bookmarks, "changed", G_CALLBACK(onBookmarksChanged), this);
|
|
||||||
|
|
||||||
// update some icons when the icon theme is changed
|
|
||||||
connect(IconTheme::instance(), &IconTheme::changed, this, &PlacesModel::updateIcons);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesModel::loadBookmarks() {
|
|
||||||
GList* allBookmarks = fm_bookmarks_get_all(bookmarks);
|
|
||||||
for(GList* l = allBookmarks; l; l = l->next) {
|
|
||||||
FmBookmarkItem* bm_item = (FmBookmarkItem*)l->data;
|
|
||||||
PlacesModelBookmarkItem* item = new PlacesModelBookmarkItem(bm_item);
|
|
||||||
bookmarksRoot->appendRow(item);
|
|
||||||
}
|
|
||||||
g_list_free_full(allBookmarks, (GDestroyNotify)fm_bookmark_item_unref);
|
|
||||||
}
|
|
||||||
|
|
||||||
PlacesModel::~PlacesModel() {
|
|
||||||
if(bookmarks) {
|
|
||||||
g_signal_handlers_disconnect_by_func(bookmarks, (gpointer)onBookmarksChanged, this);
|
|
||||||
g_object_unref(bookmarks);
|
|
||||||
}
|
|
||||||
if(volumeMonitor) {
|
|
||||||
g_signal_handlers_disconnect_by_func(volumeMonitor, (gpointer)G_CALLBACK(onVolumeAdded), this);
|
|
||||||
g_signal_handlers_disconnect_by_func(volumeMonitor, (gpointer)G_CALLBACK(onVolumeRemoved), this);
|
|
||||||
g_signal_handlers_disconnect_by_func(volumeMonitor, (gpointer)G_CALLBACK(onVolumeChanged), this);
|
|
||||||
g_signal_handlers_disconnect_by_func(volumeMonitor, (gpointer)G_CALLBACK(onMountAdded), this);
|
|
||||||
g_signal_handlers_disconnect_by_func(volumeMonitor, (gpointer)G_CALLBACK(onMountChanged), this);
|
|
||||||
g_signal_handlers_disconnect_by_func(volumeMonitor, (gpointer)G_CALLBACK(onMountRemoved), this);
|
|
||||||
g_object_unref(volumeMonitor);
|
|
||||||
}
|
|
||||||
if(trashMonitor_) {
|
|
||||||
g_signal_handlers_disconnect_by_func(trashMonitor_, (gpointer)G_CALLBACK(onTrashChanged), this);
|
|
||||||
g_object_unref(trashMonitor_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
|
||||||
void PlacesModel::onTrashChanged(GFileMonitor* monitor, GFile* gf, GFile* other, GFileMonitorEvent evt, PlacesModel* pThis) {
|
|
||||||
QTimer::singleShot(0, pThis, SLOT(updateTrash()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesModel::updateTrash() {
|
|
||||||
if(trashItem_) {
|
|
||||||
GFile* gf = fm_file_new_for_uri("trash:///");
|
|
||||||
GFileInfo* inf = g_file_query_info(gf, G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT, G_FILE_QUERY_INFO_NONE, NULL, NULL);
|
|
||||||
g_object_unref(gf);
|
|
||||||
if(inf) {
|
|
||||||
guint32 n = g_file_info_get_attribute_uint32(inf, G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT);
|
|
||||||
g_object_unref(inf);
|
|
||||||
const char* icon_name = n > 0 ? "user-trash-full" : "user-trash";
|
|
||||||
FmIcon* icon = fm_icon_from_name(icon_name);
|
|
||||||
trashItem_->setIcon(icon);
|
|
||||||
fm_icon_unref(icon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesModel::createTrashItem() {
|
|
||||||
GFile* gf;
|
|
||||||
gf = fm_file_new_for_uri("trash:///");
|
|
||||||
// check if trash is supported by the current vfs
|
|
||||||
// if gvfs is not installed, this can be unavailable.
|
|
||||||
if(!g_file_query_exists(gf, NULL)) {
|
|
||||||
g_object_unref(gf);
|
|
||||||
trashItem_ = NULL;
|
|
||||||
trashMonitor_ = NULL;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
trashItem_ = new PlacesModelItem("user-trash", tr("Trash"), fm_path_get_trash());
|
|
||||||
|
|
||||||
trashMonitor_ = fm_monitor_directory(gf, NULL);
|
|
||||||
if(trashMonitor_)
|
|
||||||
g_signal_connect(trashMonitor_, "changed", G_CALLBACK(onTrashChanged), this);
|
|
||||||
g_object_unref(gf);
|
|
||||||
|
|
||||||
placesRoot->insertRow(desktopItem->row() + 1, trashItem_);
|
|
||||||
QTimer::singleShot(0, this, SLOT(updateTrash()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesModel::setShowApplications(bool show) {
|
|
||||||
if(showApplications_ != show) {
|
|
||||||
showApplications_ = show;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesModel::setShowDesktop(bool show) {
|
|
||||||
if(showDesktop_ != show) {
|
|
||||||
showDesktop_ = show;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesModel::setShowTrash(bool show) {
|
|
||||||
if(show) {
|
|
||||||
if(!trashItem_)
|
|
||||||
createTrashItem();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(trashItem_) {
|
|
||||||
if(trashMonitor_) {
|
|
||||||
g_signal_handlers_disconnect_by_func(trashMonitor_, (gpointer)G_CALLBACK(onTrashChanged), this);
|
|
||||||
g_object_unref(trashMonitor_);
|
|
||||||
trashMonitor_ = NULL;
|
|
||||||
}
|
|
||||||
placesRoot->removeRow(trashItem_->row()); // delete trashItem_;
|
|
||||||
trashItem_ = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PlacesModelItem* PlacesModel::itemFromPath(FmPath* path) {
|
|
||||||
PlacesModelItem* item = itemFromPath(placesRoot, path);
|
|
||||||
if(!item)
|
|
||||||
item = itemFromPath(devicesRoot, path);
|
|
||||||
if(!item)
|
|
||||||
item = itemFromPath(bookmarksRoot, path);
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlacesModelItem* PlacesModel::itemFromPath(QStandardItem* rootItem, FmPath* path) {
|
|
||||||
int rowCount = rootItem->rowCount();
|
|
||||||
for(int i = 0; i < rowCount; ++i) {
|
|
||||||
PlacesModelItem* item = static_cast<PlacesModelItem*>(rootItem->child(i, 0));
|
|
||||||
if(fm_path_equal(item->path(), path))
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlacesModelVolumeItem* PlacesModel::itemFromVolume(GVolume* volume) {
|
|
||||||
int rowCount = devicesRoot->rowCount();
|
|
||||||
for(int i = 0; i < rowCount; ++i) {
|
|
||||||
PlacesModelItem* item = static_cast<PlacesModelItem*>(devicesRoot->child(i, 0));
|
|
||||||
if(item->type() == PlacesModelItem::Volume) {
|
|
||||||
PlacesModelVolumeItem* volumeItem = static_cast<PlacesModelVolumeItem*>(item);
|
|
||||||
if(volumeItem->volume() == volume)
|
|
||||||
return volumeItem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlacesModelMountItem* PlacesModel::itemFromMount(GMount* mount) {
|
|
||||||
int rowCount = devicesRoot->rowCount();
|
|
||||||
for(int i = 0; i < rowCount; ++i) {
|
|
||||||
PlacesModelItem* item = static_cast<PlacesModelItem*>(devicesRoot->child(i, 0));
|
|
||||||
if(item->type() == PlacesModelItem::Mount) {
|
|
||||||
PlacesModelMountItem* mountItem = static_cast<PlacesModelMountItem*>(item);
|
|
||||||
if(mountItem->mount() == mount)
|
|
||||||
return mountItem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlacesModelBookmarkItem* PlacesModel::itemFromBookmark(FmBookmarkItem* bkitem) {
|
|
||||||
int rowCount = bookmarksRoot->rowCount();
|
|
||||||
for(int i = 0; i < rowCount; ++i) {
|
|
||||||
PlacesModelBookmarkItem* item = static_cast<PlacesModelBookmarkItem*>(bookmarksRoot->child(i, 0));
|
|
||||||
if(item->bookmark() == bkitem)
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesModel::onMountAdded(GVolumeMonitor* monitor, GMount* mount, PlacesModel* pThis) {
|
|
||||||
GVolume* vol = g_mount_get_volume(mount);
|
|
||||||
if(vol) { // mount-added is also emitted when a volume is newly mounted.
|
|
||||||
PlacesModelVolumeItem* item = pThis->itemFromVolume(vol);
|
|
||||||
if(item && !item->path()) {
|
|
||||||
// update the mounted volume and show a button for eject.
|
|
||||||
GFile* gf = g_mount_get_root(mount);
|
|
||||||
FmPath* path = fm_path_new_for_gfile(gf);
|
|
||||||
g_object_unref(gf);
|
|
||||||
item->setPath(path);
|
|
||||||
if(path)
|
|
||||||
fm_path_unref(path);
|
|
||||||
// update the mount indicator (eject button)
|
|
||||||
QStandardItem* ejectBtn = item->parent()->child(item->row(), 1);
|
|
||||||
Q_ASSERT(ejectBtn);
|
|
||||||
ejectBtn->setIcon(pThis->ejectIcon_);
|
|
||||||
}
|
|
||||||
g_object_unref(vol);
|
|
||||||
}
|
|
||||||
else { // network mounts and others
|
|
||||||
PlacesModelMountItem* item = pThis->itemFromMount(mount);
|
|
||||||
/* for some unknown reasons, sometimes we get repeated mount-added
|
|
||||||
* signals and added a device more than one. So, make a sanity check here. */
|
|
||||||
if(!item) {
|
|
||||||
item = new PlacesModelMountItem(mount);
|
|
||||||
QStandardItem* eject_btn = new QStandardItem(pThis->ejectIcon_, "");
|
|
||||||
pThis->devicesRoot->appendRow(QList<QStandardItem*>() << item << eject_btn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesModel::onMountChanged(GVolumeMonitor* monitor, GMount* mount, PlacesModel* pThis) {
|
|
||||||
PlacesModelMountItem* item = pThis->itemFromMount(mount);
|
|
||||||
if(item)
|
|
||||||
item->update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesModel::onMountRemoved(GVolumeMonitor* monitor, GMount* mount, PlacesModel* pThis) {
|
|
||||||
GVolume* vol = g_mount_get_volume(mount);
|
|
||||||
qDebug() << "volume umounted: " << vol;
|
|
||||||
if(vol) {
|
|
||||||
// a volume is unmounted
|
|
||||||
g_object_unref(vol);
|
|
||||||
}
|
|
||||||
else { // network mounts and others
|
|
||||||
PlacesModelMountItem* item = pThis->itemFromMount(mount);
|
|
||||||
if(item) {
|
|
||||||
pThis->devicesRoot->removeRow(item->row());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesModel::onVolumeAdded(GVolumeMonitor* monitor, GVolume* volume, PlacesModel* pThis) {
|
|
||||||
// for some unknown reasons, sometimes we get repeated volume-added
|
|
||||||
// signals and added a device more than one. So, make a sanity check here.
|
|
||||||
PlacesModelVolumeItem* volumeItem = pThis->itemFromVolume(volume);
|
|
||||||
if(!volumeItem) {
|
|
||||||
volumeItem = new PlacesModelVolumeItem(volume);
|
|
||||||
QStandardItem* ejectBtn = new QStandardItem();
|
|
||||||
if(volumeItem->isMounted())
|
|
||||||
ejectBtn->setIcon(pThis->ejectIcon_);
|
|
||||||
pThis->devicesRoot->appendRow(QList<QStandardItem*>() << volumeItem << ejectBtn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesModel::onVolumeChanged(GVolumeMonitor* monitor, GVolume* volume, PlacesModel* pThis) {
|
|
||||||
PlacesModelVolumeItem* item = pThis->itemFromVolume(volume);
|
|
||||||
if(item) {
|
|
||||||
item->update();
|
|
||||||
if(!item->isMounted()) { // the volume is unmounted, remove the eject button if needed
|
|
||||||
// remove the eject button for the volume (at column 1 of the same row)
|
|
||||||
QStandardItem* ejectBtn = item->parent()->child(item->row(), 1);
|
|
||||||
Q_ASSERT(ejectBtn);
|
|
||||||
ejectBtn->setIcon(QIcon());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesModel::onVolumeRemoved(GVolumeMonitor* monitor, GVolume* volume, PlacesModel* pThis) {
|
|
||||||
PlacesModelVolumeItem* item = pThis->itemFromVolume(volume);
|
|
||||||
if(item) {
|
|
||||||
pThis->devicesRoot->removeRow(item->row());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesModel::onBookmarksChanged(FmBookmarks* bookmarks, PlacesModel* pThis) {
|
|
||||||
// remove all items
|
|
||||||
pThis->bookmarksRoot->removeRows(0, pThis->bookmarksRoot->rowCount());
|
|
||||||
pThis->loadBookmarks();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesModel::updateIcons() {
|
|
||||||
// the icon theme is changed and we need to update the icons
|
|
||||||
PlacesModelItem* item;
|
|
||||||
int row;
|
|
||||||
int n = placesRoot->rowCount();
|
|
||||||
for(row = 0; row < n; ++row) {
|
|
||||||
item = static_cast<PlacesModelItem*>(placesRoot->child(row));
|
|
||||||
item->updateIcon();
|
|
||||||
}
|
|
||||||
n = devicesRoot->rowCount();
|
|
||||||
for(row = 0; row < n; ++row) {
|
|
||||||
item = static_cast<PlacesModelItem*>(devicesRoot->child(row));
|
|
||||||
item->updateIcon();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt::ItemFlags PlacesModel::flags(const QModelIndex& index) const {
|
|
||||||
if(index.column() == 1) // make 2nd column of every row selectable.
|
|
||||||
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
|
|
||||||
if(!index.parent().isValid()) { // root items
|
|
||||||
if(index.row() == 2) // bookmarks root
|
|
||||||
return Qt::ItemIsEnabled | Qt::ItemIsDropEnabled;
|
|
||||||
else
|
|
||||||
return Qt::ItemIsEnabled;
|
|
||||||
}
|
|
||||||
return QStandardItemModel::flags(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool PlacesModel::dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent) {
|
|
||||||
QStandardItem* item = itemFromIndex(parent);
|
|
||||||
if(data->hasFormat("application/x-bookmark-row")) { // the data being dopped is a bookmark row
|
|
||||||
// decode it and do bookmark reordering
|
|
||||||
QByteArray buf = data->data("application/x-bookmark-row");
|
|
||||||
QDataStream stream(&buf, QIODevice::ReadOnly);
|
|
||||||
int oldPos = -1;
|
|
||||||
char* pathStr = NULL;
|
|
||||||
stream >> oldPos >> pathStr;
|
|
||||||
// find the source bookmark item being dragged
|
|
||||||
GList* allBookmarks = fm_bookmarks_get_all(bookmarks);
|
|
||||||
FmBookmarkItem* draggedItem = static_cast<FmBookmarkItem*>(g_list_nth_data(allBookmarks, oldPos));
|
|
||||||
// If we cannot find the dragged bookmark item at position <oldRow>, or we find an item,
|
|
||||||
// but the path of the item is not the same as what we expected, than it's the wrong item.
|
|
||||||
// This means that the bookmarks are changed during our dnd processing, which is an extremely rare case.
|
|
||||||
if(!draggedItem || !fm_path_equal_str(draggedItem->path, pathStr, -1)) {
|
|
||||||
delete []pathStr;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
delete []pathStr;
|
|
||||||
|
|
||||||
int newPos = -1;
|
|
||||||
if(row == -1 && column == -1) { // drop on an item
|
|
||||||
// we only allow dropping on an bookmark item
|
|
||||||
if(item && item->parent() == bookmarksRoot)
|
|
||||||
newPos = parent.row();
|
|
||||||
}
|
|
||||||
else { // drop on a position between items
|
|
||||||
if(item == bookmarksRoot) // we only allow dropping on a bookmark item
|
|
||||||
newPos = row;
|
|
||||||
}
|
|
||||||
if(newPos != -1 && newPos != oldPos) // reorder the bookmark item
|
|
||||||
fm_bookmarks_reorder(bookmarks, draggedItem, newPos);
|
|
||||||
}
|
|
||||||
else if(data->hasUrls()) { // files uris are dropped
|
|
||||||
if(row == -1 && column == -1) { // drop uris on an item
|
|
||||||
if(item && item->parent()) { // need to be a child item
|
|
||||||
PlacesModelItem* placesItem = static_cast<PlacesModelItem*>(item);
|
|
||||||
if(placesItem->path()) {
|
|
||||||
qDebug() << "dropped dest:" << placesItem->text();
|
|
||||||
// TODO: copy or move the dragged files to the dir pointed by the item.
|
|
||||||
qDebug() << "drop on" << item->text();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else { // drop uris on a position between items
|
|
||||||
if(item == bookmarksRoot) { // we only allow dropping on blank row of bookmarks section
|
|
||||||
FmPathList* paths = pathListFromQUrls(data->urls());
|
|
||||||
for(GList* l = fm_path_list_peek_head_link(paths); l; l = l->next) {
|
|
||||||
FmPath* path = FM_PATH(l->data);
|
|
||||||
GFile* gf = fm_path_to_gfile(path);
|
|
||||||
// FIXME: this is a blocking call
|
|
||||||
if(g_file_query_file_type(gf, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
|
||||||
NULL) == G_FILE_TYPE_DIRECTORY) {
|
|
||||||
char* disp_name = fm_path_display_basename(path);
|
|
||||||
fm_bookmarks_insert(bookmarks, path, disp_name, row);
|
|
||||||
g_free(disp_name);
|
|
||||||
}
|
|
||||||
g_object_unref(gf);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we only support dragging bookmark items and use our own
|
|
||||||
// custom pseudo-mime-type: application/x-bookmark-row
|
|
||||||
QMimeData* PlacesModel::mimeData(const QModelIndexList& indexes) const {
|
|
||||||
if(!indexes.isEmpty()) {
|
|
||||||
// we only allow dragging one bookmark item at a time, so handle the first index only.
|
|
||||||
QModelIndex index = indexes.first();
|
|
||||||
QStandardItem* item = itemFromIndex(index);
|
|
||||||
// ensure that it's really a bookmark item
|
|
||||||
if(item && item->parent() == bookmarksRoot) {
|
|
||||||
PlacesModelBookmarkItem* bookmarkItem = static_cast<PlacesModelBookmarkItem*>(item);
|
|
||||||
QMimeData* mime = new QMimeData();
|
|
||||||
QByteArray data;
|
|
||||||
QDataStream stream(&data, QIODevice::WriteOnly);
|
|
||||||
// There is no safe and cross-process way to store a reference of a row.
|
|
||||||
// Let's store the pos, name, and path of the bookmark item instead.
|
|
||||||
char* pathStr = fm_path_to_str(bookmarkItem->path());
|
|
||||||
stream << index.row() << pathStr;
|
|
||||||
g_free(pathStr);
|
|
||||||
mime->setData("application/x-bookmark-row", data);
|
|
||||||
return mime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList PlacesModel::mimeTypes() const {
|
|
||||||
return QStringList() << "application/x-bookmark-row" << "text/uri-list";
|
|
||||||
}
|
|
||||||
|
|
||||||
Qt::DropActions PlacesModel::supportedDropActions() const {
|
|
||||||
return QStandardItemModel::supportedDropActions();
|
|
||||||
}
|
|
||||||
|
|
@ -1,131 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef FM_PLACESMODEL_H
|
|
||||||
#define FM_PLACESMODEL_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QStandardItemModel>
|
|
||||||
#include <QStandardItem>
|
|
||||||
#include <QList>
|
|
||||||
#include <QAction>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class PlacesModelItem;
|
|
||||||
class PlacesModelVolumeItem;
|
|
||||||
class PlacesModelMountItem;
|
|
||||||
class PlacesModelBookmarkItem;
|
|
||||||
|
|
||||||
class LIBFM_QT_API PlacesModel : public QStandardItemModel {
|
|
||||||
Q_OBJECT
|
|
||||||
friend class PlacesView;
|
|
||||||
public:
|
|
||||||
|
|
||||||
// QAction used for popup menus
|
|
||||||
class ItemAction : public QAction {
|
|
||||||
public:
|
|
||||||
ItemAction(const QModelIndex& index, QString text, QObject* parent = 0):
|
|
||||||
QAction(text, parent),
|
|
||||||
index_(index) {
|
|
||||||
}
|
|
||||||
|
|
||||||
QPersistentModelIndex& index() {
|
|
||||||
return index_;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
QPersistentModelIndex index_;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit PlacesModel(QObject* parent = 0);
|
|
||||||
virtual ~PlacesModel();
|
|
||||||
|
|
||||||
bool showTrash() {
|
|
||||||
return trashItem_ != NULL;
|
|
||||||
}
|
|
||||||
void setShowTrash(bool show);
|
|
||||||
|
|
||||||
bool showApplications() {
|
|
||||||
return showApplications_;
|
|
||||||
}
|
|
||||||
void setShowApplications(bool show);
|
|
||||||
|
|
||||||
bool showDesktop() {
|
|
||||||
return showDesktop_;
|
|
||||||
}
|
|
||||||
void setShowDesktop(bool show);
|
|
||||||
|
|
||||||
public Q_SLOTS:
|
|
||||||
void updateIcons();
|
|
||||||
void updateTrash();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
PlacesModelItem* itemFromPath(FmPath* path);
|
|
||||||
PlacesModelItem* itemFromPath(QStandardItem* rootItem, FmPath* path);
|
|
||||||
PlacesModelVolumeItem* itemFromVolume(GVolume* volume);
|
|
||||||
PlacesModelMountItem* itemFromMount(GMount* mount);
|
|
||||||
PlacesModelBookmarkItem* itemFromBookmark(FmBookmarkItem* bkitem);
|
|
||||||
|
|
||||||
virtual Qt::ItemFlags flags(const QModelIndex& index) const;
|
|
||||||
virtual QStringList mimeTypes() const;
|
|
||||||
virtual QMimeData* mimeData(const QModelIndexList& indexes) const;
|
|
||||||
virtual bool dropMimeData(const QMimeData* data, Qt::DropAction action, int row, int column, const QModelIndex& parent);
|
|
||||||
Qt::DropActions supportedDropActions() const;
|
|
||||||
|
|
||||||
void createTrashItem();
|
|
||||||
|
|
||||||
private:
|
|
||||||
void loadBookmarks();
|
|
||||||
|
|
||||||
static void onVolumeAdded(GVolumeMonitor* monitor, GVolume* volume, PlacesModel* pThis);
|
|
||||||
static void onVolumeRemoved(GVolumeMonitor* monitor, GVolume* volume, PlacesModel* pThis);
|
|
||||||
static void onVolumeChanged(GVolumeMonitor* monitor, GVolume* volume, PlacesModel* pThis);
|
|
||||||
static void onMountAdded(GVolumeMonitor* monitor, GMount* mount, PlacesModel* pThis);
|
|
||||||
static void onMountRemoved(GVolumeMonitor* monitor, GMount* mount, PlacesModel* pThis);
|
|
||||||
static void onMountChanged(GVolumeMonitor* monitor, GMount* mount, PlacesModel* pThis);
|
|
||||||
|
|
||||||
static void onBookmarksChanged(FmBookmarks* bookmarks, PlacesModel* pThis);
|
|
||||||
|
|
||||||
static void onTrashChanged(GFileMonitor *monitor, GFile *gf, GFile *other, GFileMonitorEvent evt, PlacesModel* pThis);
|
|
||||||
private:
|
|
||||||
FmBookmarks* bookmarks;
|
|
||||||
GVolumeMonitor* volumeMonitor;
|
|
||||||
QList<FmJob*> jobs;
|
|
||||||
bool showApplications_;
|
|
||||||
bool showDesktop_;
|
|
||||||
QStandardItem* placesRoot;
|
|
||||||
QStandardItem* devicesRoot;
|
|
||||||
QStandardItem* bookmarksRoot;
|
|
||||||
PlacesModelItem* trashItem_;
|
|
||||||
GFileMonitor* trashMonitor_;
|
|
||||||
PlacesModelItem* desktopItem;
|
|
||||||
PlacesModelItem* homeItem;
|
|
||||||
PlacesModelItem* computerItem;
|
|
||||||
PlacesModelItem* networkItem;
|
|
||||||
PlacesModelItem* applicationsItem;
|
|
||||||
QIcon ejectIcon_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_PLACESMODEL_H
|
|
@ -1,190 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "placesmodelitem.h"
|
|
||||||
#include "icontheme.h"
|
|
||||||
#include <gio/gio.h>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
PlacesModelItem::PlacesModelItem():
|
|
||||||
QStandardItem(),
|
|
||||||
path_(NULL),
|
|
||||||
icon_(NULL),
|
|
||||||
fileInfo_(NULL) {
|
|
||||||
}
|
|
||||||
|
|
||||||
PlacesModelItem::PlacesModelItem(const char* iconName, QString title, FmPath* path):
|
|
||||||
QStandardItem(title),
|
|
||||||
path_(path ? fm_path_ref(path) : NULL),
|
|
||||||
icon_(fm_icon_from_name(iconName)),
|
|
||||||
fileInfo_(NULL) {
|
|
||||||
if(icon_)
|
|
||||||
QStandardItem::setIcon(IconTheme::icon(icon_));
|
|
||||||
setEditable(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
PlacesModelItem::PlacesModelItem(FmIcon* icon, QString title, FmPath* path):
|
|
||||||
QStandardItem(title),
|
|
||||||
path_(path ? fm_path_ref(path) : NULL),
|
|
||||||
icon_(icon ? fm_icon_ref(icon) : NULL),
|
|
||||||
fileInfo_(NULL) {
|
|
||||||
if(icon_)
|
|
||||||
QStandardItem::setIcon(IconTheme::icon(icon));
|
|
||||||
setEditable(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
PlacesModelItem::PlacesModelItem(QIcon icon, QString title, FmPath* path):
|
|
||||||
QStandardItem(icon, title),
|
|
||||||
icon_(NULL),
|
|
||||||
path_(path ? fm_path_ref(path) : NULL),
|
|
||||||
fileInfo_(NULL) {
|
|
||||||
setEditable(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
PlacesModelItem::~PlacesModelItem() {
|
|
||||||
if(path_)
|
|
||||||
fm_path_unref(path_);
|
|
||||||
if(fileInfo_)
|
|
||||||
g_object_unref(fileInfo_);
|
|
||||||
if(icon_)
|
|
||||||
fm_icon_unref(icon_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesModelItem::setPath(FmPath* path) {
|
|
||||||
if(path_)
|
|
||||||
fm_path_unref(path_);
|
|
||||||
path_ = path ? fm_path_ref(path) : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesModelItem::setIcon(FmIcon* icon) {
|
|
||||||
if(icon_)
|
|
||||||
fm_icon_unref(icon_);
|
|
||||||
if(icon) {
|
|
||||||
icon_ = fm_icon_ref(icon);
|
|
||||||
QStandardItem::setIcon(IconTheme::icon(icon_));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
icon_ = NULL;
|
|
||||||
QStandardItem::setIcon(QIcon());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesModelItem::setIcon(GIcon* gicon) {
|
|
||||||
FmIcon* icon = gicon ? fm_icon_from_gicon(gicon) : NULL;
|
|
||||||
setIcon(icon);
|
|
||||||
fm_icon_unref(icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesModelItem::updateIcon() {
|
|
||||||
if(icon_)
|
|
||||||
QStandardItem::setIcon(IconTheme::icon(icon_));
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant PlacesModelItem::data(int role) const {
|
|
||||||
// we use a QPixmap from FmIcon cache rather than QIcon object for decoration role.
|
|
||||||
return QStandardItem::data(role);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesModelItem::setFileInfo(FmFileInfo* fileInfo) {
|
|
||||||
// FIXME: how can we correctly update icon?
|
|
||||||
if(fileInfo_)
|
|
||||||
fm_file_info_unref(fileInfo_);
|
|
||||||
|
|
||||||
if(fileInfo) {
|
|
||||||
fileInfo_ = fm_file_info_ref(fileInfo);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
fileInfo_ = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlacesModelBookmarkItem::PlacesModelBookmarkItem(FmBookmarkItem* bm_item):
|
|
||||||
PlacesModelItem(QIcon::fromTheme("folder"), QString::fromUtf8(bm_item->name), bm_item->path),
|
|
||||||
bookmarkItem_(fm_bookmark_item_ref(bm_item)) {
|
|
||||||
setEditable(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
PlacesModelVolumeItem::PlacesModelVolumeItem(GVolume* volume):
|
|
||||||
PlacesModelItem(),
|
|
||||||
volume_(reinterpret_cast<GVolume*>(g_object_ref(volume))) {
|
|
||||||
update();
|
|
||||||
setEditable(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesModelVolumeItem::update() {
|
|
||||||
// set title
|
|
||||||
char* volumeName = g_volume_get_name(volume_);
|
|
||||||
setText(QString::fromUtf8(volumeName));
|
|
||||||
g_free(volumeName);
|
|
||||||
|
|
||||||
// set icon
|
|
||||||
GIcon* gicon = g_volume_get_icon(volume_);
|
|
||||||
setIcon(gicon);
|
|
||||||
g_object_unref(gicon);
|
|
||||||
|
|
||||||
// set dir path
|
|
||||||
GMount* mount = g_volume_get_mount(volume_);
|
|
||||||
if(mount) {
|
|
||||||
GFile* mount_root = g_mount_get_root(mount);
|
|
||||||
FmPath* mount_path = fm_path_new_for_gfile(mount_root);
|
|
||||||
setPath(mount_path);
|
|
||||||
fm_path_unref(mount_path);
|
|
||||||
g_object_unref(mount_root);
|
|
||||||
g_object_unref(mount);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setPath(NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool PlacesModelVolumeItem::isMounted() {
|
|
||||||
GMount* mount = g_volume_get_mount(volume_);
|
|
||||||
if(mount)
|
|
||||||
g_object_unref(mount);
|
|
||||||
return mount != NULL ? true : false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
PlacesModelMountItem::PlacesModelMountItem(GMount* mount):
|
|
||||||
PlacesModelItem(),
|
|
||||||
mount_(reinterpret_cast<GMount*>(mount)) {
|
|
||||||
update();
|
|
||||||
setEditable(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesModelMountItem::update() {
|
|
||||||
// set title
|
|
||||||
setText(QString::fromUtf8(g_mount_get_name(mount_)));
|
|
||||||
|
|
||||||
// set path
|
|
||||||
GFile* mount_root = g_mount_get_root(mount_);
|
|
||||||
FmPath* mount_path = fm_path_new_for_gfile(mount_root);
|
|
||||||
setPath(mount_path);
|
|
||||||
fm_path_unref(mount_path);
|
|
||||||
g_object_unref(mount_root);
|
|
||||||
|
|
||||||
// set icon
|
|
||||||
GIcon* gicon = g_mount_get_icon(mount_);
|
|
||||||
setIcon(gicon);
|
|
||||||
g_object_unref(gicon);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,130 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef FM_PLACESMODELITEM_H
|
|
||||||
#define FM_PLACESMODELITEM_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QStandardItemModel>
|
|
||||||
#include <QStandardItem>
|
|
||||||
#include <QList>
|
|
||||||
#include <QAction>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
// model item
|
|
||||||
class LIBFM_QT_API PlacesModelItem : public QStandardItem {
|
|
||||||
public:
|
|
||||||
enum Type {
|
|
||||||
Places = QStandardItem::UserType + 1,
|
|
||||||
Volume,
|
|
||||||
Mount,
|
|
||||||
Bookmark
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
PlacesModelItem();
|
|
||||||
PlacesModelItem(QIcon icon, QString title, FmPath* path = NULL);
|
|
||||||
PlacesModelItem(const char* iconName, QString title, FmPath* path = NULL);
|
|
||||||
PlacesModelItem(FmIcon* icon, QString title, FmPath* path = NULL);
|
|
||||||
~PlacesModelItem();
|
|
||||||
|
|
||||||
FmFileInfo* fileInfo() {
|
|
||||||
return fileInfo_;
|
|
||||||
}
|
|
||||||
void setFileInfo(FmFileInfo* fileInfo);
|
|
||||||
|
|
||||||
FmPath* path() {
|
|
||||||
return path_;
|
|
||||||
}
|
|
||||||
void setPath(FmPath* path);
|
|
||||||
|
|
||||||
FmIcon* icon() {
|
|
||||||
return icon_;
|
|
||||||
}
|
|
||||||
void setIcon(FmIcon* icon);
|
|
||||||
void setIcon(GIcon* gicon);
|
|
||||||
void updateIcon();
|
|
||||||
|
|
||||||
QVariant data(int role = Qt::UserRole + 1) const;
|
|
||||||
|
|
||||||
virtual int type() const {
|
|
||||||
return Places;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
FmPath* path_;
|
|
||||||
FmFileInfo* fileInfo_;
|
|
||||||
FmIcon* icon_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class LIBFM_QT_API PlacesModelVolumeItem : public PlacesModelItem {
|
|
||||||
public:
|
|
||||||
PlacesModelVolumeItem(GVolume* volume);
|
|
||||||
bool isMounted();
|
|
||||||
bool canEject() {
|
|
||||||
return g_volume_can_eject(volume_);
|
|
||||||
}
|
|
||||||
virtual int type() const {
|
|
||||||
return Volume;
|
|
||||||
}
|
|
||||||
GVolume* volume() {
|
|
||||||
return volume_;
|
|
||||||
}
|
|
||||||
void update();
|
|
||||||
private:
|
|
||||||
GVolume* volume_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class LIBFM_QT_API PlacesModelMountItem : public PlacesModelItem {
|
|
||||||
public:
|
|
||||||
PlacesModelMountItem(GMount* mount);
|
|
||||||
virtual int type() const {
|
|
||||||
return Mount;
|
|
||||||
}
|
|
||||||
GMount* mount() const {
|
|
||||||
return mount_;
|
|
||||||
}
|
|
||||||
void update();
|
|
||||||
private:
|
|
||||||
GMount* mount_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class LIBFM_QT_API PlacesModelBookmarkItem : public PlacesModelItem {
|
|
||||||
public:
|
|
||||||
virtual int type() const {
|
|
||||||
return Bookmark;
|
|
||||||
}
|
|
||||||
PlacesModelBookmarkItem(FmBookmarkItem* bm_item);
|
|
||||||
virtual ~PlacesModelBookmarkItem() {
|
|
||||||
if(bookmarkItem_)
|
|
||||||
fm_bookmark_item_unref(bookmarkItem_);
|
|
||||||
}
|
|
||||||
FmBookmarkItem* bookmark() const {
|
|
||||||
return bookmarkItem_;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
FmBookmarkItem* bookmarkItem_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_PLACESMODELITEM_H
|
|
@ -1,418 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2012 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "placesview.h"
|
|
||||||
#include "placesmodel.h"
|
|
||||||
#include "placesmodelitem.h"
|
|
||||||
#include "mountoperation.h"
|
|
||||||
#include "fileoperation.h"
|
|
||||||
#include <QMenu>
|
|
||||||
#include <QContextMenuEvent>
|
|
||||||
#include <QHeaderView>
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QGuiApplication>
|
|
||||||
|
|
||||||
using namespace Fm;
|
|
||||||
|
|
||||||
PlacesView::PlacesView(QWidget* parent):
|
|
||||||
QTreeView(parent),
|
|
||||||
currentPath_(NULL) {
|
|
||||||
setRootIsDecorated(false);
|
|
||||||
setHeaderHidden(true);
|
|
||||||
setIndentation(12);
|
|
||||||
|
|
||||||
connect(this, &QTreeView::clicked, this, &PlacesView::onClicked);
|
|
||||||
connect(this, &QTreeView::pressed, this, &PlacesView::onPressed);
|
|
||||||
|
|
||||||
setIconSize(QSize(24, 24));
|
|
||||||
|
|
||||||
// FIXME: we may share this model amont all views
|
|
||||||
model_ = new PlacesModel(this);
|
|
||||||
setModel(model_);
|
|
||||||
|
|
||||||
QHeaderView* headerView = header();
|
|
||||||
headerView->setSectionResizeMode(0, QHeaderView::Stretch);
|
|
||||||
headerView->setSectionResizeMode(1, QHeaderView::Fixed);
|
|
||||||
headerView->setStretchLastSection(false);
|
|
||||||
expandAll();
|
|
||||||
|
|
||||||
// FIXME: is there any better way to make the first column span the whole row?
|
|
||||||
setFirstColumnSpanned(0, QModelIndex(), true); // places root
|
|
||||||
setFirstColumnSpanned(1, QModelIndex(), true); // devices root
|
|
||||||
setFirstColumnSpanned(2, QModelIndex(), true); // bookmarks root
|
|
||||||
|
|
||||||
// the 2nd column is for the eject buttons
|
|
||||||
setSelectionBehavior(QAbstractItemView::SelectRows); // FIXME: why this does not work?
|
|
||||||
setAllColumnsShowFocus(false);
|
|
||||||
|
|
||||||
setAcceptDrops(true);
|
|
||||||
setDragEnabled(true);
|
|
||||||
|
|
||||||
// update the umount button's column width based on icon size
|
|
||||||
onIconSizeChanged(iconSize());
|
|
||||||
#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) // this signal requires Qt >= 5.5
|
|
||||||
connect(this, &QAbstractItemView::iconSizeChanged, this, &PlacesView::onIconSizeChanged);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
PlacesView::~PlacesView() {
|
|
||||||
if(currentPath_)
|
|
||||||
fm_path_unref(currentPath_);
|
|
||||||
// qDebug("delete PlacesView");
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesView::activateRow(int type, const QModelIndex& index) {
|
|
||||||
if(!index.parent().isValid()) // ignore root items
|
|
||||||
return;
|
|
||||||
PlacesModelItem* item = static_cast<PlacesModelItem*>(model_->itemFromIndex(index));
|
|
||||||
if(item) {
|
|
||||||
FmPath* path = item->path();
|
|
||||||
if(!path) {
|
|
||||||
// check if mounting volumes is needed
|
|
||||||
if(item->type() == PlacesModelItem::Volume) {
|
|
||||||
PlacesModelVolumeItem* volumeItem = static_cast<PlacesModelVolumeItem*>(item);
|
|
||||||
if(!volumeItem->isMounted()) {
|
|
||||||
// Mount the volume
|
|
||||||
GVolume* volume = volumeItem->volume();
|
|
||||||
MountOperation* op = new MountOperation(true, this);
|
|
||||||
op->mount(volume);
|
|
||||||
// connect(op, SIGNAL(finished(GError*)), SLOT(onMountOperationFinished(GError*)));
|
|
||||||
// blocking here until the mount operation is finished?
|
|
||||||
|
|
||||||
// FIXME: update status of the volume after mount is finished!!
|
|
||||||
if(!op->wait())
|
|
||||||
return;
|
|
||||||
path = item->path();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(path) {
|
|
||||||
Q_EMIT chdirRequested(type, path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// mouse button pressed
|
|
||||||
void PlacesView::onPressed(const QModelIndex& index) {
|
|
||||||
// if middle button is pressed
|
|
||||||
if(QGuiApplication::mouseButtons() & Qt::MiddleButton) {
|
|
||||||
// the real item is at column 0
|
|
||||||
activateRow(1, 0 == index.column() ? index : index.sibling(index.row(), 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesView::onIconSizeChanged(const QSize& size) {
|
|
||||||
setColumnWidth(1, size.width() + 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesView::onEjectButtonClicked(PlacesModelItem* item) {
|
|
||||||
// The eject button is clicked for a device item (volume or mount)
|
|
||||||
if(item->type() == PlacesModelItem::Volume) {
|
|
||||||
PlacesModelVolumeItem* volumeItem = static_cast<PlacesModelVolumeItem*>(item);
|
|
||||||
MountOperation* op = new MountOperation(true, this);
|
|
||||||
if(volumeItem->canEject()) // do eject if applicable
|
|
||||||
op->eject(volumeItem->volume());
|
|
||||||
else // otherwise, do unmount instead
|
|
||||||
op->unmount(volumeItem->volume());
|
|
||||||
}
|
|
||||||
else if(item->type() == PlacesModelItem::Mount) {
|
|
||||||
PlacesModelMountItem* mountItem = static_cast<PlacesModelMountItem*>(item);
|
|
||||||
MountOperation* op = new MountOperation(true, this);
|
|
||||||
op->unmount(mountItem->mount());
|
|
||||||
}
|
|
||||||
qDebug("PlacesView::onEjectButtonClicked");
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesView::onClicked(const QModelIndex& index) {
|
|
||||||
if(!index.parent().isValid()) // ignore root items
|
|
||||||
return;
|
|
||||||
|
|
||||||
if(index.column() == 0) {
|
|
||||||
activateRow(0, index);
|
|
||||||
}
|
|
||||||
else if(index.column() == 1) { // column 1 contains eject buttons of the mounted devices
|
|
||||||
if(index.parent() == model_->devicesRoot->index()) { // this is a mounted device
|
|
||||||
// the eject button is clicked
|
|
||||||
QModelIndex itemIndex = index.sibling(index.row(), 0); // the real item is at column 0
|
|
||||||
PlacesModelItem* item = static_cast<PlacesModelItem*>(model_->itemFromIndex(itemIndex));
|
|
||||||
if(item) {
|
|
||||||
// eject the volume or the mount
|
|
||||||
onEjectButtonClicked(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
activateRow(0, index.sibling(index.row(), 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesView::setCurrentPath(FmPath* path) {
|
|
||||||
if(currentPath_)
|
|
||||||
fm_path_unref(currentPath_);
|
|
||||||
if(path) {
|
|
||||||
currentPath_ = fm_path_ref(path);
|
|
||||||
// TODO: search for item with the path in model_ and select it.
|
|
||||||
PlacesModelItem* item = model_->itemFromPath(currentPath_);
|
|
||||||
if(item) {
|
|
||||||
selectionModel()->select(item->index(), QItemSelectionModel::SelectCurrent|QItemSelectionModel::Rows);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
clearSelection();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
currentPath_ = NULL;
|
|
||||||
clearSelection();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PlacesView::dragMoveEvent(QDragMoveEvent* event) {
|
|
||||||
QTreeView::dragMoveEvent(event);
|
|
||||||
/*
|
|
||||||
QModelIndex index = indexAt(event->pos());
|
|
||||||
if(event->isAccepted() && index.isValid() && index.parent() == model_->bookmarksRoot->index()) {
|
|
||||||
if(dropIndicatorPosition() != OnItem) {
|
|
||||||
event->setDropAction(Qt::LinkAction);
|
|
||||||
event->accept();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesView::dropEvent(QDropEvent* event) {
|
|
||||||
QTreeView::dropEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesView::onEmptyTrash() {
|
|
||||||
FmPathList* files = fm_path_list_new();
|
|
||||||
fm_path_list_push_tail(files, fm_path_get_trash());
|
|
||||||
Fm::FileOperation::deleteFiles(files);
|
|
||||||
fm_path_list_unref(files);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesView::onMoveBookmarkUp()
|
|
||||||
{
|
|
||||||
PlacesModel::ItemAction* action = static_cast<PlacesModel::ItemAction*>(sender());
|
|
||||||
if(!action->index().isValid())
|
|
||||||
return;
|
|
||||||
PlacesModelBookmarkItem* item = static_cast<PlacesModelBookmarkItem*>(model_->itemFromIndex(action->index()));
|
|
||||||
|
|
||||||
int row = item->row();
|
|
||||||
if(row > 0) {
|
|
||||||
FmBookmarkItem* bookmarkItem = item->bookmark();
|
|
||||||
FmBookmarks* bookmarks = fm_bookmarks_dup();
|
|
||||||
fm_bookmarks_reorder(bookmarks, bookmarkItem, row - 1);
|
|
||||||
g_object_unref(bookmarks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesView::onMoveBookmarkDown()
|
|
||||||
{
|
|
||||||
PlacesModel::ItemAction* action = static_cast<PlacesModel::ItemAction*>(sender());
|
|
||||||
if(!action->index().isValid())
|
|
||||||
return;
|
|
||||||
PlacesModelBookmarkItem* item = static_cast<PlacesModelBookmarkItem*>(model_->itemFromIndex(action->index()));
|
|
||||||
|
|
||||||
int row = item->row();
|
|
||||||
if(row < model_->rowCount()) {
|
|
||||||
FmBookmarkItem* bookmarkItem = item->bookmark();
|
|
||||||
FmBookmarks* bookmarks = fm_bookmarks_dup();
|
|
||||||
fm_bookmarks_reorder(bookmarks, bookmarkItem, row + 1);
|
|
||||||
g_object_unref(bookmarks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesView::onDeleteBookmark() {
|
|
||||||
PlacesModel::ItemAction* action = static_cast<PlacesModel::ItemAction*>(sender());
|
|
||||||
if(!action->index().isValid())
|
|
||||||
return;
|
|
||||||
PlacesModelBookmarkItem* item = static_cast<PlacesModelBookmarkItem*>(model_->itemFromIndex(action->index()));
|
|
||||||
FmBookmarkItem* bookmarkItem = item->bookmark();
|
|
||||||
FmBookmarks* bookmarks = fm_bookmarks_dup();
|
|
||||||
fm_bookmarks_remove(bookmarks, bookmarkItem);
|
|
||||||
g_object_unref(bookmarks);
|
|
||||||
}
|
|
||||||
|
|
||||||
// virtual
|
|
||||||
void PlacesView::commitData(QWidget * editor) {
|
|
||||||
QTreeView::commitData(editor);
|
|
||||||
PlacesModelBookmarkItem* item = static_cast<PlacesModelBookmarkItem*>(model_->itemFromIndex(currentIndex()));
|
|
||||||
FmBookmarkItem* bookmarkItem = item->bookmark();
|
|
||||||
FmBookmarks* bookmarks = fm_bookmarks_dup();
|
|
||||||
// rename bookmark
|
|
||||||
fm_bookmarks_rename(bookmarks, bookmarkItem, item->text().toUtf8().constData());
|
|
||||||
g_object_unref(bookmarks);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesView::onOpenNewTab()
|
|
||||||
{
|
|
||||||
PlacesModel::ItemAction* action = static_cast<PlacesModel::ItemAction*>(sender());
|
|
||||||
if(!action->index().isValid())
|
|
||||||
return;
|
|
||||||
PlacesModelItem* item = static_cast<PlacesModelItem*>(model_->itemFromIndex(action->index()));
|
|
||||||
if(item)
|
|
||||||
Q_EMIT chdirRequested(1, item->path());
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesView::onOpenNewWindow()
|
|
||||||
{
|
|
||||||
PlacesModel::ItemAction* action = static_cast<PlacesModel::ItemAction*>(sender());
|
|
||||||
if(!action->index().isValid())
|
|
||||||
return;
|
|
||||||
PlacesModelItem* item = static_cast<PlacesModelItem*>(model_->itemFromIndex(action->index()));
|
|
||||||
if(item)
|
|
||||||
Q_EMIT chdirRequested(2, item->path());
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesView::onRenameBookmark() {
|
|
||||||
PlacesModel::ItemAction* action = static_cast<PlacesModel::ItemAction*>(sender());
|
|
||||||
if(!action->index().isValid())
|
|
||||||
return;
|
|
||||||
PlacesModelBookmarkItem* item = static_cast<PlacesModelBookmarkItem*>(model_->itemFromIndex(action->index()));
|
|
||||||
setFocus();
|
|
||||||
setCurrentIndex(item->index());
|
|
||||||
edit(item->index());
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesView::onMountVolume() {
|
|
||||||
PlacesModel::ItemAction* action = static_cast<PlacesModel::ItemAction*>(sender());
|
|
||||||
if(!action->index().isValid())
|
|
||||||
return;
|
|
||||||
PlacesModelVolumeItem* item = static_cast<PlacesModelVolumeItem*>(model_->itemFromIndex(action->index()));
|
|
||||||
MountOperation* op = new MountOperation(true, this);
|
|
||||||
op->mount(item->volume());
|
|
||||||
op->wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesView::onUnmountVolume() {
|
|
||||||
PlacesModel::ItemAction* action = static_cast<PlacesModel::ItemAction*>(sender());
|
|
||||||
if(!action->index().isValid())
|
|
||||||
return;
|
|
||||||
PlacesModelVolumeItem* item = static_cast<PlacesModelVolumeItem*>(model_->itemFromIndex(action->index()));
|
|
||||||
MountOperation* op = new MountOperation(true, this);
|
|
||||||
op->unmount(item->volume());
|
|
||||||
op->wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesView::onUnmountMount() {
|
|
||||||
PlacesModel::ItemAction* action = static_cast<PlacesModel::ItemAction*>(sender());
|
|
||||||
if(!action->index().isValid())
|
|
||||||
return;
|
|
||||||
PlacesModelMountItem* item = static_cast<PlacesModelMountItem*>(model_->itemFromIndex(action->index()));
|
|
||||||
GMount* mount = item->mount();
|
|
||||||
MountOperation* op = new MountOperation(true, this);
|
|
||||||
op->unmount(mount);
|
|
||||||
op->wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesView::onEjectVolume() {
|
|
||||||
PlacesModel::ItemAction* action = static_cast<PlacesModel::ItemAction*>(sender());
|
|
||||||
if(!action->index().isValid())
|
|
||||||
return;
|
|
||||||
PlacesModelVolumeItem* item = static_cast<PlacesModelVolumeItem*>(model_->itemFromIndex(action->index()));
|
|
||||||
MountOperation* op = new MountOperation(true, this);
|
|
||||||
op->eject(item->volume());
|
|
||||||
op->wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlacesView::contextMenuEvent(QContextMenuEvent* event) {
|
|
||||||
QModelIndex index = indexAt(event->pos());
|
|
||||||
if(index.isValid() && index.parent().isValid()) {
|
|
||||||
if(index.column() != 0) // the real item is at column 0
|
|
||||||
index = index.sibling(index.row(), 0);
|
|
||||||
QMenu* menu = new QMenu(this);
|
|
||||||
QAction* action;
|
|
||||||
PlacesModelItem* item = static_cast<PlacesModelItem*>(model_->itemFromIndex(index));
|
|
||||||
|
|
||||||
if(item->type() != PlacesModelItem::Mount
|
|
||||||
&& (item->type() != PlacesModelItem::Volume
|
|
||||||
|| static_cast<PlacesModelVolumeItem*>(item)->isMounted())) {
|
|
||||||
action = new PlacesModel::ItemAction(item->index(), tr("Open in New Tab"), menu);
|
|
||||||
connect(action, &QAction::triggered, this, &PlacesView::onOpenNewTab);
|
|
||||||
menu->addAction(action);
|
|
||||||
action = new PlacesModel::ItemAction(item->index(), tr("Open in New Window"), menu);
|
|
||||||
connect(action, &QAction::triggered, this, &PlacesView::onOpenNewWindow);
|
|
||||||
menu->addAction(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(item->type()) {
|
|
||||||
case PlacesModelItem::Places: {
|
|
||||||
FmPath* path = item->path();
|
|
||||||
if(path && fm_path_equal(fm_path_get_trash(), path)) {
|
|
||||||
action = new PlacesModel::ItemAction(item->index(), tr("Empty Trash"), menu);
|
|
||||||
connect(action, &QAction::triggered, this, &PlacesView::onEmptyTrash);
|
|
||||||
menu->addAction(action);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PlacesModelItem::Bookmark: {
|
|
||||||
// create context menu for bookmark item
|
|
||||||
if(item->index().row() > 0) {
|
|
||||||
action = new PlacesModel::ItemAction(item->index(), tr("Move Bookmark Up"), menu);
|
|
||||||
connect(action, &QAction::triggered, this, &PlacesView::onMoveBookmarkUp);
|
|
||||||
menu->addAction(action);
|
|
||||||
}
|
|
||||||
if(item->index().row() < model_->rowCount()) {
|
|
||||||
action = new PlacesModel::ItemAction(item->index(), tr("Move Bookmark Down"), menu);
|
|
||||||
connect(action, &QAction::triggered, this, &PlacesView::onMoveBookmarkDown);
|
|
||||||
menu->addAction(action);
|
|
||||||
}
|
|
||||||
action = new PlacesModel::ItemAction(item->index(), tr("Rename Bookmark"), menu);
|
|
||||||
connect(action, &QAction::triggered, this, &PlacesView::onRenameBookmark);
|
|
||||||
menu->addAction(action);
|
|
||||||
action = new PlacesModel::ItemAction(item->index(), tr("Remove Bookmark"), menu);
|
|
||||||
connect(action, &QAction::triggered, this, &PlacesView::onDeleteBookmark);
|
|
||||||
menu->addAction(action);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PlacesModelItem::Volume: {
|
|
||||||
PlacesModelVolumeItem* volumeItem = static_cast<PlacesModelVolumeItem*>(item);
|
|
||||||
|
|
||||||
if(volumeItem->isMounted()) {
|
|
||||||
action = new PlacesModel::ItemAction(item->index(), tr("Unmount"), menu);
|
|
||||||
connect(action, &QAction::triggered, this, &PlacesView::onUnmountVolume);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
action = new PlacesModel::ItemAction(item->index(), tr("Mount"), menu);
|
|
||||||
connect(action, &QAction::triggered, this, &PlacesView::onMountVolume);
|
|
||||||
}
|
|
||||||
menu->addAction(action);
|
|
||||||
|
|
||||||
if(volumeItem->canEject()) {
|
|
||||||
action = new PlacesModel::ItemAction(item->index(), tr("Eject"), menu);
|
|
||||||
connect(action, &QAction::triggered, this, &PlacesView::onEjectVolume);
|
|
||||||
menu->addAction(action);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PlacesModelItem::Mount: {
|
|
||||||
action = new PlacesModel::ItemAction(item->index(), tr("Unmount"), menu);
|
|
||||||
connect(action, &QAction::triggered, this, &PlacesView::onUnmountMount);
|
|
||||||
menu->addAction(action);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(menu->actions().size()) {
|
|
||||||
menu->popup(mapToGlobal(event->pos()));
|
|
||||||
connect(menu, &QMenu::aboutToHide, menu, &QMenu::deleteLater);
|
|
||||||
} else {
|
|
||||||
menu->deleteLater();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,109 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2012 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef FM_PLACESVIEW_H
|
|
||||||
#define FM_PLACESVIEW_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QTreeView>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class PlacesModel;
|
|
||||||
class PlacesModelItem;
|
|
||||||
|
|
||||||
class LIBFM_QT_API PlacesView : public QTreeView {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit PlacesView(QWidget* parent = 0);
|
|
||||||
virtual ~PlacesView();
|
|
||||||
|
|
||||||
void setCurrentPath(FmPath* path);
|
|
||||||
FmPath* currentPath() {
|
|
||||||
return currentPath_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// libfm-gtk compatible alias
|
|
||||||
FmPath* getCwd() {
|
|
||||||
return currentPath();
|
|
||||||
}
|
|
||||||
|
|
||||||
void chdir(FmPath* path) {
|
|
||||||
setCurrentPath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(5, 5, 0)
|
|
||||||
void setIconSize(const QSize &size) {
|
|
||||||
// The signal QAbstractItemView::iconSizeChanged is only available after Qt 5.5.
|
|
||||||
// To simulate the effect for older Qt versions, we override setIconSize().
|
|
||||||
QAbstractItemView::setIconSize(size);
|
|
||||||
onIconSizeChanged(size);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void chdirRequested(int type, FmPath* path);
|
|
||||||
|
|
||||||
protected Q_SLOTS:
|
|
||||||
void onClicked(const QModelIndex & index);
|
|
||||||
void onPressed(const QModelIndex & index);
|
|
||||||
void onIconSizeChanged(const QSize & size);
|
|
||||||
// void onMountOperationFinished(GError* error);
|
|
||||||
|
|
||||||
void onOpenNewTab();
|
|
||||||
void onOpenNewWindow();
|
|
||||||
|
|
||||||
void onEmptyTrash();
|
|
||||||
|
|
||||||
void onMountVolume();
|
|
||||||
void onUnmountVolume();
|
|
||||||
void onEjectVolume();
|
|
||||||
void onUnmountMount();
|
|
||||||
|
|
||||||
void onMoveBookmarkUp();
|
|
||||||
void onMoveBookmarkDown();
|
|
||||||
void onDeleteBookmark();
|
|
||||||
void onRenameBookmark();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void drawBranches ( QPainter * painter, const QRect & rect, const QModelIndex & index ) const {
|
|
||||||
// override this method to inhibit drawing of the branch grid lines by Qt.
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void dragMoveEvent(QDragMoveEvent* event);
|
|
||||||
virtual void dropEvent(QDropEvent* event);
|
|
||||||
virtual void contextMenuEvent(QContextMenuEvent* event);
|
|
||||||
|
|
||||||
virtual void commitData(QWidget * editor);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void onEjectButtonClicked(PlacesModelItem* item);
|
|
||||||
void activateRow(int type, const QModelIndex& index);
|
|
||||||
|
|
||||||
private:
|
|
||||||
PlacesModel* model_;
|
|
||||||
FmPath* currentPath_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_PLACESVIEW_H
|
|
@ -1,278 +0,0 @@
|
|||||||
/*
|
|
||||||
<one line to give the library's name and an idea of what it does.>
|
|
||||||
Copyright (C) 2012 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 "proxyfoldermodel.h"
|
|
||||||
#include "foldermodel.h"
|
|
||||||
#include <QCollator>
|
|
||||||
|
|
||||||
using namespace Fm;
|
|
||||||
|
|
||||||
ProxyFolderModel::ProxyFolderModel(QObject * parent):
|
|
||||||
QSortFilterProxyModel(parent),
|
|
||||||
thumbnailSize_(0),
|
|
||||||
showHidden_(false),
|
|
||||||
showThumbnails_(false),
|
|
||||||
folderFirst_(true) {
|
|
||||||
setDynamicSortFilter(true);
|
|
||||||
setSortCaseSensitivity(Qt::CaseInsensitive);
|
|
||||||
}
|
|
||||||
|
|
||||||
ProxyFolderModel::~ProxyFolderModel() {
|
|
||||||
qDebug("delete ProxyFolderModel");
|
|
||||||
|
|
||||||
if(showThumbnails_ && thumbnailSize_ != 0) {
|
|
||||||
FolderModel* srcModel = static_cast<FolderModel*>(sourceModel());
|
|
||||||
// tell the source model that we don't need the thumnails anymore
|
|
||||||
if(srcModel) {
|
|
||||||
srcModel->releaseThumbnails(thumbnailSize_);
|
|
||||||
disconnect(srcModel, SIGNAL(thumbnailLoaded(QModelIndex,int)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProxyFolderModel::setSourceModel(QAbstractItemModel* model) {
|
|
||||||
if(model) {
|
|
||||||
// we only support Fm::FolderModel
|
|
||||||
Q_ASSERT(model->inherits("Fm::FolderModel"));
|
|
||||||
|
|
||||||
if(showThumbnails_ && thumbnailSize_ != 0) { // if we're showing thumbnails
|
|
||||||
FolderModel* oldSrcModel = static_cast<FolderModel*>(sourceModel());
|
|
||||||
FolderModel* newSrcModel = static_cast<FolderModel*>(model);
|
|
||||||
if(oldSrcModel) { // we need to release cached thumbnails for the old source model
|
|
||||||
oldSrcModel->releaseThumbnails(thumbnailSize_);
|
|
||||||
disconnect(oldSrcModel, SIGNAL(thumbnailLoaded(QModelIndex,int)));
|
|
||||||
}
|
|
||||||
if(newSrcModel) { // tell the new source model that we want thumbnails of this size
|
|
||||||
newSrcModel->cacheThumbnails(thumbnailSize_);
|
|
||||||
connect(newSrcModel, &FolderModel::thumbnailLoaded, this, &ProxyFolderModel::onThumbnailLoaded);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
QSortFilterProxyModel::setSourceModel(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProxyFolderModel::sort(int column, Qt::SortOrder order) {
|
|
||||||
int oldColumn = sortColumn();
|
|
||||||
Qt::SortOrder oldOrder = sortOrder();
|
|
||||||
QSortFilterProxyModel::sort(column, order);
|
|
||||||
if(column != oldColumn || order != oldOrder) {
|
|
||||||
Q_EMIT sortFilterChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProxyFolderModel::setShowHidden(bool show) {
|
|
||||||
if(show != showHidden_) {
|
|
||||||
showHidden_ = show;
|
|
||||||
invalidateFilter();
|
|
||||||
Q_EMIT sortFilterChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// need to call invalidateFilter() manually.
|
|
||||||
void ProxyFolderModel::setFolderFirst(bool folderFirst) {
|
|
||||||
if(folderFirst != folderFirst_) {
|
|
||||||
folderFirst_ = folderFirst;
|
|
||||||
invalidate();
|
|
||||||
Q_EMIT sortFilterChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ProxyFolderModel::filterAcceptsRow(int source_row, const QModelIndex & source_parent) const {
|
|
||||||
if(!showHidden_) {
|
|
||||||
QAbstractItemModel* srcModel = sourceModel();
|
|
||||||
QString name = srcModel->data(srcModel->index(source_row, 0, source_parent)).toString();
|
|
||||||
if(name.startsWith(".") || name.endsWith("~"))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// apply additional filters if there're any
|
|
||||||
Q_FOREACH(ProxyFolderModelFilter* filter, filters_) {
|
|
||||||
FolderModel* srcModel = static_cast<FolderModel*>(sourceModel());
|
|
||||||
FmFileInfo* fileInfo = srcModel->fileInfoFromIndex(srcModel->index(source_row, 0, source_parent));
|
|
||||||
if(!filter->filterAcceptsRow(this, fileInfo))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ProxyFolderModel::lessThan(const QModelIndex& left, const QModelIndex& right) const {
|
|
||||||
FolderModel* srcModel = static_cast<FolderModel*>(sourceModel());
|
|
||||||
// left and right are indexes of source model, not the proxy model.
|
|
||||||
if(srcModel) {
|
|
||||||
FmFileInfo* leftInfo = srcModel->fileInfoFromIndex(left);
|
|
||||||
FmFileInfo* rightInfo = srcModel->fileInfoFromIndex(right);
|
|
||||||
|
|
||||||
if(Q_UNLIKELY(!leftInfo || !rightInfo)) {
|
|
||||||
// In theory, this should not happen, but it's safer to add the null check.
|
|
||||||
// This is reported in https://github.com/lxde/pcmanfm-qt/issues/205
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(folderFirst_) {
|
|
||||||
bool leftIsFolder = (bool)fm_file_info_is_dir(leftInfo);
|
|
||||||
bool rightIsFolder = (bool)fm_file_info_is_dir(rightInfo);
|
|
||||||
if(leftIsFolder != rightIsFolder)
|
|
||||||
return sortOrder() == Qt::AscendingOrder ? leftIsFolder : rightIsFolder;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(sortColumn()) {
|
|
||||||
case FolderModel::ColumnFileName:
|
|
||||||
if(sortCaseSensitivity() == Qt::CaseSensitive) {
|
|
||||||
// fm_file_info_get_collate_key_nocasefold() uses g_utf8_casefold() from glib internally, which
|
|
||||||
// is only an approximation not working correctly in some locales.
|
|
||||||
// FIXME: we may use QCollator (since Qt 5.2) for this, but the performance impact is unknown
|
|
||||||
return strcmp(fm_file_info_get_collate_key_nocasefold(leftInfo), fm_file_info_get_collate_key_nocasefold(rightInfo)) < 0;
|
|
||||||
/*
|
|
||||||
QCollator coll;
|
|
||||||
coll.setCaseSensitivity(Qt::CaseSensitive);
|
|
||||||
coll.setIgnorePunctuation(false);
|
|
||||||
coll.setNumericMode(true);
|
|
||||||
return coll.compare(QString::fromUtf8(fm_file_info_get_disp_name(leftInfo)), QString::fromUtf8(fm_file_info_get_disp_name(rightInfo))) < 0;
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// linguistic case insensitive ordering
|
|
||||||
return strcmp(fm_file_info_get_collate_key(leftInfo), fm_file_info_get_collate_key(rightInfo)) < 0;
|
|
||||||
}
|
|
||||||
case FolderModel::ColumnFileMTime:
|
|
||||||
return fm_file_info_get_mtime(leftInfo) < fm_file_info_get_mtime(rightInfo);
|
|
||||||
case FolderModel::ColumnFileSize:
|
|
||||||
return fm_file_info_get_size(leftInfo) < fm_file_info_get_size(rightInfo);
|
|
||||||
case FolderModel::ColumnFileOwner:
|
|
||||||
// TODO: sort by owner
|
|
||||||
break;
|
|
||||||
case FolderModel::ColumnFileType:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return QSortFilterProxyModel::lessThan(left, right);
|
|
||||||
}
|
|
||||||
|
|
||||||
FmFileInfo* ProxyFolderModel::fileInfoFromIndex(const QModelIndex& index) const {
|
|
||||||
FolderModel* srcModel = static_cast<FolderModel*>(sourceModel());
|
|
||||||
if(srcModel) {
|
|
||||||
QModelIndex srcIndex = mapToSource(index);
|
|
||||||
return srcModel->fileInfoFromIndex(srcIndex);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProxyFolderModel::setShowThumbnails(bool show) {
|
|
||||||
if(show != showThumbnails_) {
|
|
||||||
showThumbnails_ = show;
|
|
||||||
FolderModel* srcModel = static_cast<FolderModel*>(sourceModel());
|
|
||||||
if(srcModel && thumbnailSize_ != 0) {
|
|
||||||
if(show) {
|
|
||||||
// ask for cache of thumbnails of the new size in source model
|
|
||||||
srcModel->cacheThumbnails(thumbnailSize_);
|
|
||||||
// connect to the srcModel so we can be notified when a thumbnail is loaded.
|
|
||||||
connect(srcModel, &FolderModel::thumbnailLoaded, this, &ProxyFolderModel::onThumbnailLoaded);
|
|
||||||
}
|
|
||||||
else { // turn off thumbnails
|
|
||||||
// free cached old thumbnails in souce model
|
|
||||||
srcModel->releaseThumbnails(thumbnailSize_);
|
|
||||||
disconnect(srcModel, SIGNAL(thumbnailLoaded(QModelIndex,int)));
|
|
||||||
}
|
|
||||||
// reload all items, FIXME: can we only update items previously having thumbnails
|
|
||||||
Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProxyFolderModel::setThumbnailSize(int size) {
|
|
||||||
if(size != thumbnailSize_) {
|
|
||||||
FolderModel* srcModel = static_cast<FolderModel*>(sourceModel());
|
|
||||||
if(showThumbnails_ && srcModel) {
|
|
||||||
// free cached thumbnails of the old size
|
|
||||||
if(thumbnailSize_ != 0)
|
|
||||||
srcModel->releaseThumbnails(thumbnailSize_);
|
|
||||||
else {
|
|
||||||
// if the old thumbnail size is 0, we did not turn on thumbnail initially
|
|
||||||
connect(srcModel, &FolderModel::thumbnailLoaded, this, &ProxyFolderModel::onThumbnailLoaded);
|
|
||||||
}
|
|
||||||
// ask for cache of thumbnails of the new size in source model
|
|
||||||
srcModel->cacheThumbnails(size);
|
|
||||||
// reload all items, FIXME: can we only update items previously having thumbnails
|
|
||||||
Q_EMIT dataChanged(index(0, 0), index(rowCount() - 1, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
thumbnailSize_ = size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant ProxyFolderModel::data(const QModelIndex& index, int role) const {
|
|
||||||
if(index.column() == 0) { // only show the decoration role for the first column
|
|
||||||
if(role == Qt::DecorationRole && showThumbnails_ && thumbnailSize_) {
|
|
||||||
// we need to show thumbnails instead of icons
|
|
||||||
FolderModel* srcModel = static_cast<FolderModel*>(sourceModel());
|
|
||||||
QModelIndex srcIndex = mapToSource(index);
|
|
||||||
QImage image = srcModel->thumbnailFromIndex(srcIndex, thumbnailSize_);
|
|
||||||
if(!image.isNull()) // if we got a thumbnail of the desired size, use it
|
|
||||||
return QVariant(image);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// fallback to icons if thumbnails are not available
|
|
||||||
return QSortFilterProxyModel::data(index, role);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProxyFolderModel::onThumbnailLoaded(const QModelIndex& srcIndex, int size) {
|
|
||||||
// FolderModel* srcModel = static_cast<FolderModel*>(sourceModel());
|
|
||||||
// FolderModelItem* item = srcModel->itemFromIndex(srcIndex);
|
|
||||||
// qDebug("ProxyFolderModel::onThumbnailLoaded: %d, %s", size, item->displayName.toUtf8().data());
|
|
||||||
|
|
||||||
if(size == thumbnailSize_) { // if a thumbnail of the size we want is loaded
|
|
||||||
QModelIndex index = mapFromSource(srcIndex);
|
|
||||||
Q_EMIT dataChanged(index, index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProxyFolderModel::addFilter(ProxyFolderModelFilter* filter) {
|
|
||||||
filters_.append(filter);
|
|
||||||
invalidateFilter();
|
|
||||||
Q_EMIT sortFilterChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProxyFolderModel::removeFilter(ProxyFolderModelFilter* filter) {
|
|
||||||
filters_.removeOne(filter);
|
|
||||||
invalidateFilter();
|
|
||||||
Q_EMIT sortFilterChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProxyFolderModel::updateFilters() {
|
|
||||||
invalidate();
|
|
||||||
Q_EMIT sortFilterChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
void ProxyFolderModel::reloadAllThumbnails() {
|
|
||||||
// reload all thumbnails and update UI
|
|
||||||
FolderModel* srcModel = static_cast<FolderModel*>(sourceModel());
|
|
||||||
if(srcModel) {
|
|
||||||
int rows= rowCount();
|
|
||||||
for(int row = 0; row < rows; ++row) {
|
|
||||||
QModelIndex index = this->index(row, 0);
|
|
||||||
QModelIndex srcIndex = mapToSource(index);
|
|
||||||
QImage image = srcModel->thumbnailFromIndex(srcIndex, size);
|
|
||||||
// tell the world that the item is changed to trigger a UI update
|
|
||||||
if(!image.isNull())
|
|
||||||
Q_EMIT dataChanged(index, index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
@ -1,109 +0,0 @@
|
|||||||
/*
|
|
||||||
<one line to give the library's name and an idea of what it does.>
|
|
||||||
Copyright (C) 2012 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_PROXYFOLDERMODEL_H
|
|
||||||
#define FM_PROXYFOLDERMODEL_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QSortFilterProxyModel>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
#include <QList>
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
// a proxy model used to sort and filter FolderModel
|
|
||||||
|
|
||||||
class FolderModelItem;
|
|
||||||
class ProxyFolderModel;
|
|
||||||
|
|
||||||
class LIBFM_QT_API ProxyFolderModelFilter {
|
|
||||||
public:
|
|
||||||
virtual bool filterAcceptsRow(const ProxyFolderModel* model, FmFileInfo* info) const = 0;
|
|
||||||
virtual ~ProxyFolderModelFilter() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
class LIBFM_QT_API ProxyFolderModel : public QSortFilterProxyModel {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit ProxyFolderModel(QObject * parent = 0);
|
|
||||||
virtual ~ProxyFolderModel();
|
|
||||||
|
|
||||||
// only Fm::FolderModel is allowed for being sourceModel
|
|
||||||
virtual void setSourceModel(QAbstractItemModel* model);
|
|
||||||
|
|
||||||
void setShowHidden(bool show);
|
|
||||||
bool showHidden() const {
|
|
||||||
return showHidden_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setFolderFirst(bool folderFirst);
|
|
||||||
bool folderFirst() {
|
|
||||||
return folderFirst_;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setSortCaseSensitivity(Qt::CaseSensitivity cs) {
|
|
||||||
QSortFilterProxyModel::setSortCaseSensitivity(cs);
|
|
||||||
Q_EMIT sortFilterChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool showThumbnails() {
|
|
||||||
return showThumbnails_;
|
|
||||||
}
|
|
||||||
void setShowThumbnails(bool show);
|
|
||||||
|
|
||||||
int thumbnailSize() {
|
|
||||||
return thumbnailSize_;
|
|
||||||
}
|
|
||||||
void setThumbnailSize(int size);
|
|
||||||
|
|
||||||
FmFileInfo* fileInfoFromIndex(const QModelIndex& index) const;
|
|
||||||
|
|
||||||
virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
|
|
||||||
virtual QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
|
|
||||||
|
|
||||||
void addFilter(ProxyFolderModelFilter* filter);
|
|
||||||
void removeFilter(ProxyFolderModelFilter* filter);
|
|
||||||
void updateFilters();
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void sortFilterChanged();
|
|
||||||
|
|
||||||
protected Q_SLOTS:
|
|
||||||
void onThumbnailLoaded(const QModelIndex& srcIndex, int size);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const;
|
|
||||||
bool lessThan(const QModelIndex & left, const QModelIndex & right) const;
|
|
||||||
// void reloadAllThumbnails();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool showHidden_;
|
|
||||||
bool folderFirst_;
|
|
||||||
bool showThumbnails_;
|
|
||||||
int thumbnailSize_;
|
|
||||||
QList<ProxyFolderModelFilter*> filters_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_PROXYFOLDERMODEL_H
|
|
@ -1,204 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<ui version="4.0">
|
|
||||||
<class>RenameDialog</class>
|
|
||||||
<widget class="QDialog" name="RenameDialog">
|
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>398</width>
|
|
||||||
<height>220</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<property name="windowTitle">
|
|
||||||
<string>Confirm to replace files</string>
|
|
||||||
</property>
|
|
||||||
<property name="sizeGripEnabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
|
||||||
<property name="spacing">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<property name="margin">
|
|
||||||
<number>10</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string><html><head/><body><p><span style=" font-weight:600;">There is already a file with the same name in this location.</span></p><p>Do you want to replace the existing file?</p></body></html></string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QGridLayout" name="gridLayout">
|
|
||||||
<property name="horizontalSpacing">
|
|
||||||
<number>12</number>
|
|
||||||
</property>
|
|
||||||
<property name="verticalSpacing">
|
|
||||||
<number>6</number>
|
|
||||||
</property>
|
|
||||||
<item row="0" column="0">
|
|
||||||
<widget class="QLabel" name="destIcon">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>dest</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="1" column="0" colspan="2">
|
|
||||||
<widget class="QLabel" name="label_3">
|
|
||||||
<property name="text">
|
|
||||||
<string>with the following file?</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="1">
|
|
||||||
<widget class="QLabel" name="srcInfo">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>src file info</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="0" column="1">
|
|
||||||
<widget class="QLabel" name="destInfo">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>dest file info</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item row="2" column="0">
|
|
||||||
<widget class="QLabel" name="srcIcon">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>src</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
|
||||||
<property name="spacing">
|
|
||||||
<number>12</number>
|
|
||||||
</property>
|
|
||||||
<item>
|
|
||||||
<widget class="QLabel" name="label_6">
|
|
||||||
<property name="sizePolicy">
|
|
||||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
|
||||||
<horstretch>0</horstretch>
|
|
||||||
<verstretch>0</verstretch>
|
|
||||||
</sizepolicy>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>&File name:</string>
|
|
||||||
</property>
|
|
||||||
<property name="buddy">
|
|
||||||
<cstring>fileName</cstring>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QLineEdit" name="fileName"/>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QCheckBox" name="applyToAll">
|
|
||||||
<property name="text">
|
|
||||||
<string>Apply this option to all existing files</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="verticalSpacer">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeType">
|
|
||||||
<enum>QSizePolicy::Expanding</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>0</width>
|
|
||||||
<height>0</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QDialogButtonBox" name="buttonBox">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Horizontal</enum>
|
|
||||||
</property>
|
|
||||||
<property name="standardButtons">
|
|
||||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ignore|QDialogButtonBox::Ok</set>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</widget>
|
|
||||||
<resources/>
|
|
||||||
<connections>
|
|
||||||
<connection>
|
|
||||||
<sender>buttonBox</sender>
|
|
||||||
<signal>accepted()</signal>
|
|
||||||
<receiver>RenameDialog</receiver>
|
|
||||||
<slot>accept()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>248</x>
|
|
||||||
<y>254</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>157</x>
|
|
||||||
<y>274</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
<connection>
|
|
||||||
<sender>buttonBox</sender>
|
|
||||||
<signal>rejected()</signal>
|
|
||||||
<receiver>RenameDialog</receiver>
|
|
||||||
<slot>reject()</slot>
|
|
||||||
<hints>
|
|
||||||
<hint type="sourcelabel">
|
|
||||||
<x>316</x>
|
|
||||||
<y>260</y>
|
|
||||||
</hint>
|
|
||||||
<hint type="destinationlabel">
|
|
||||||
<x>286</x>
|
|
||||||
<y>274</y>
|
|
||||||
</hint>
|
|
||||||
</hints>
|
|
||||||
</connection>
|
|
||||||
</connections>
|
|
||||||
</ui>
|
|
@ -1,137 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#include "renamedialog.h"
|
|
||||||
#include "ui_rename-dialog.h"
|
|
||||||
#include <QStringBuilder>
|
|
||||||
#include <QPushButton>
|
|
||||||
#include "icontheme.h"
|
|
||||||
|
|
||||||
using namespace Fm;
|
|
||||||
|
|
||||||
RenameDialog::RenameDialog(FmFileInfo* src, FmFileInfo* dest, QWidget* parent, Qt::WindowFlags f):
|
|
||||||
QDialog(parent, f),
|
|
||||||
action_(ActionIgnore),
|
|
||||||
applyToAll_(false) {
|
|
||||||
|
|
||||||
ui = new Ui::RenameDialog();
|
|
||||||
ui->setupUi(this);
|
|
||||||
|
|
||||||
FmPath* path = fm_file_info_get_path(dest);
|
|
||||||
FmIcon* srcIcon = fm_file_info_get_icon(src);
|
|
||||||
FmIcon* destIcon = fm_file_info_get_icon(dest);
|
|
||||||
|
|
||||||
// show info for the source file
|
|
||||||
QIcon icon = IconTheme::icon(srcIcon);
|
|
||||||
QSize iconSize(fm_config->big_icon_size, fm_config->big_icon_size);
|
|
||||||
QPixmap pixmap = icon.pixmap(iconSize);
|
|
||||||
ui->srcIcon->setPixmap(pixmap);
|
|
||||||
|
|
||||||
QString infoStr;
|
|
||||||
const char* disp_size = fm_file_info_get_disp_size(src);
|
|
||||||
if(disp_size) {
|
|
||||||
infoStr = QString(tr("Type: %1\nSize: %2\nModified: %3"))
|
|
||||||
.arg(QString::fromUtf8(fm_file_info_get_desc(src)))
|
|
||||||
.arg(QString::fromUtf8(disp_size))
|
|
||||||
.arg(QString::fromUtf8(fm_file_info_get_disp_mtime(src)));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
infoStr = QString(tr("Type: %1\nModified: %2"))
|
|
||||||
.arg(QString::fromUtf8(fm_file_info_get_desc(src)))
|
|
||||||
.arg(QString::fromUtf8(fm_file_info_get_disp_mtime(src)));
|
|
||||||
}
|
|
||||||
ui->srcInfo->setText(infoStr);
|
|
||||||
|
|
||||||
// show info for the dest file
|
|
||||||
icon = IconTheme::icon(destIcon);
|
|
||||||
pixmap = icon.pixmap(iconSize);
|
|
||||||
ui->destIcon->setPixmap(pixmap);
|
|
||||||
|
|
||||||
disp_size = fm_file_info_get_disp_size(dest);
|
|
||||||
if(disp_size) {
|
|
||||||
infoStr = QString(tr("Type: %1\nSize: %2\nModified: %3"))
|
|
||||||
.arg(QString::fromUtf8(fm_file_info_get_desc(dest)))
|
|
||||||
.arg(QString::fromUtf8(disp_size))
|
|
||||||
.arg(QString::fromUtf8(fm_file_info_get_disp_mtime(dest)));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
infoStr = QString(tr("Type: %1\nModified: %3"))
|
|
||||||
.arg(QString::fromUtf8(fm_file_info_get_desc(src)))
|
|
||||||
.arg(QString::fromUtf8(fm_file_info_get_disp_mtime(src)));
|
|
||||||
}
|
|
||||||
ui->destInfo->setText(infoStr);
|
|
||||||
|
|
||||||
char* basename = fm_path_display_basename(path);
|
|
||||||
ui->fileName->setText(QString::fromUtf8(basename));
|
|
||||||
oldName_ = basename;
|
|
||||||
g_free(basename);
|
|
||||||
connect(ui->fileName, &QLineEdit::textChanged, this, &RenameDialog::onFileNameChanged);
|
|
||||||
|
|
||||||
// add "Rename" button
|
|
||||||
QAbstractButton* button = ui->buttonBox->button(QDialogButtonBox::Ok);
|
|
||||||
button->setText(tr("&Overwrite"));
|
|
||||||
// FIXME: there seems to be no way to place the Rename button next to the overwrite one.
|
|
||||||
renameButton_ = ui->buttonBox->addButton(tr("&Rename"), QDialogButtonBox::ActionRole);
|
|
||||||
connect(renameButton_, &QPushButton::clicked, this, &RenameDialog::onRenameClicked);
|
|
||||||
renameButton_->setEnabled(false); // disabled by default
|
|
||||||
|
|
||||||
button = ui->buttonBox->button(QDialogButtonBox::Ignore);
|
|
||||||
connect(button, &QPushButton::clicked, this, &RenameDialog::onIgnoreClicked);
|
|
||||||
}
|
|
||||||
|
|
||||||
RenameDialog::~RenameDialog() {
|
|
||||||
delete ui;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenameDialog::onRenameClicked() {
|
|
||||||
action_ = ActionRename;
|
|
||||||
QDialog::done(QDialog::Accepted);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenameDialog::onIgnoreClicked() {
|
|
||||||
action_ = ActionIgnore;
|
|
||||||
}
|
|
||||||
|
|
||||||
// the overwrite button
|
|
||||||
void RenameDialog::accept() {
|
|
||||||
action_ = ActionOverwrite;
|
|
||||||
applyToAll_ = ui->applyToAll->isChecked();
|
|
||||||
QDialog::accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
// cancel, or close the dialog
|
|
||||||
void RenameDialog::reject() {
|
|
||||||
action_ = ActionCancel;
|
|
||||||
QDialog::reject();
|
|
||||||
}
|
|
||||||
|
|
||||||
void RenameDialog::onFileNameChanged(QString newName) {
|
|
||||||
newName_ = newName;
|
|
||||||
// FIXME: check if the name already exists in the current dir
|
|
||||||
bool hasNewName = (newName_ != oldName_);
|
|
||||||
renameButton_->setEnabled(hasNewName);
|
|
||||||
renameButton_->setDefault(hasNewName);
|
|
||||||
|
|
||||||
// change default button to rename rather than overwrire
|
|
||||||
// if the user typed a new filename
|
|
||||||
QPushButton* overwriteButton = static_cast<QPushButton*>(ui->buttonBox->button(QDialogButtonBox::Ok));
|
|
||||||
overwriteButton->setEnabled(!hasNewName);
|
|
||||||
overwriteButton->setDefault(!hasNewName);
|
|
||||||
}
|
|
@ -1,83 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
Copyright (C) 2013 Hong Jen Yee (PCMan) <pcman.tw@gmail.com>
|
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License along
|
|
||||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef FM_RENAMEDIALOG_H
|
|
||||||
#define FM_RENAMEDIALOG_H
|
|
||||||
|
|
||||||
#include "libfmqtglobals.h"
|
|
||||||
#include <QDialog>
|
|
||||||
#include <libfm/fm.h>
|
|
||||||
|
|
||||||
namespace Ui {
|
|
||||||
class RenameDialog;
|
|
||||||
};
|
|
||||||
|
|
||||||
class QPushButton;
|
|
||||||
|
|
||||||
namespace Fm {
|
|
||||||
|
|
||||||
class LIBFM_QT_API RenameDialog : public QDialog {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
enum Action {
|
|
||||||
ActionCancel,
|
|
||||||
ActionRename,
|
|
||||||
ActionOverwrite,
|
|
||||||
ActionIgnore
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit RenameDialog(FmFileInfo* src, FmFileInfo* dest, QWidget* parent = 0, Qt::WindowFlags f = 0);
|
|
||||||
virtual ~RenameDialog();
|
|
||||||
|
|
||||||
Action action() {
|
|
||||||
return action_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool applyToAll() {
|
|
||||||
return applyToAll_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString newName() {
|
|
||||||
return newName_;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Q_SLOTS:
|
|
||||||
void onRenameClicked();
|
|
||||||
void onIgnoreClicked();
|
|
||||||
void onFileNameChanged(QString newName);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void accept();
|
|
||||||
void reject();
|
|
||||||
|
|
||||||
private:
|
|
||||||
Ui::RenameDialog* ui;
|
|
||||||
QPushButton* renameButton_;
|
|
||||||
Action action_;
|
|
||||||
bool applyToAll_;
|
|
||||||
QString oldName_;
|
|
||||||
QString newName_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // FM_RENAMEDIALOG_H
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue