commit a18dbd2927b67e672ede07b776b40d17d55ea0c0 Author: ChangZhuo Chen (陳昌倬) Date: Fri Dec 18 00:43:39 2015 +0800 Imported Upstream version 2.0.0+20151214 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bf867f3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +build*/ + +# Vim.gitignore +[._]*.s[a-w][a-z] +[._]s[a-w][a-z] +*.un~ +Session.vim +.netrwhist +*~ diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..b5b60c5 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,6 @@ +Upstream Authors: + LXQt team: http://lxqt.org + Hong Jen Yee (PCMan) + +Copyright: + Copyright (c) 2013-2015 LXQt team diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..9b254f4 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,131 @@ +cmake_minimum_required(VERSION 3.0.2) +project(libfm-qt) + +set(LIBFM_QT_LIBRARY_NAME "fm-qt" CACHE STRING "fm-qt") + +set(LIBFM_QT_VERSION_MAJOR 0) +set(LIBFM_QT_VERSION_MINOR 10) +set(LIBFM_QT_VERSION_PATCH 0) +set(LIBFM_QT_VERSION ${LIBFM_QT_VERSION_MAJOR}.${LIBFM_QT_VERSION_MINOR}.${LIBFM_QT_VERSION_PATCH}) + +list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") + +# We use the libtool versioning scheme for the internal so name, "current:revision:age" +# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info +# https://www.sourceware.org/autobook/autobook/autobook_91.html +# http://pusling.com/blog/?p=352 +# Actually, libtool uses different ways on different operating systems. So there is no +# universal way to translate a libtool version-info to a cmake version. +# We use "(current-age).age.revision" as the cmake version. +# current: 2, revision: 0, age: 0 => version: 2.0.0 +set(LIBFM_QT_LIB_VERSION "2.0.0") +set(LIBFM_QT_LIB_SOVERSION "2") + +set(REQUIRED_QT_VERSION "5.2") +set(REQUIRED_LIBFM_VERSION "1.2.0") +set(REQUIRED_LIBMENUCACHE_VERSION "0.4.0") + +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +find_package(Qt5Widgets "${REQUIRED_QT_VERSION}" REQUIRED) +find_package(Qt5LinguistTools "${REQUIRED_QT_VERSION}" REQUIRED) +find_package(Qt5X11Extras "${REQUIRED_QT_VERSION}" REQUIRED) + +find_package(PkgConfig) +pkg_check_modules(SYSTEM_LIBS REQUIRED + glib-2.0 + gio-2.0 + gio-unix-2.0 +) + +pkg_check_modules(LIBFM REQUIRED libfm>="${REQUIRED_LIBFM_VERSION}") +pkg_check_modules(LIBMENUCACHE REQUIRED libmenu-cache>="${REQUIRED_LIBMENUCACHE_VERSION}") + +option(UPDATE_TRANSLATIONS "Update source translation translations/*.ts files" OFF) +include(GNUInstallDirs) +include(GenerateExportHeader) +include(CMakePackageConfigHelpers) +include(LXQtTranslateTs) +include(LXQtTranslateDesktop) + +add_definitions(-DQT_NO_KEYWORDS) + +# set visibility to hidden to hide symbols, unless they're exported manually in the code +set(CMAKE_CXX_VISIBILITY_PRESET hidden) +set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) + +if (CMAKE_VERSION VERSION_LESS "3.1") + include(CheckCXXCompilerFlag) + CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11) + if(COMPILER_SUPPORTS_CXX11) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + else() + CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X) + # -std=c++0x is deprecated but some tools e.g. qmake or older gcc are still using it + if(COMPILER_SUPPORTS_CXX0X) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") + else() + message(FATAL_ERROR "Compiler ${CMAKE_CXX_COMPILER} does not support c++11/c++0x") + endif() + endif() +else() + set(CMAKE_CXX_STANDARD 11) +endif() + +if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + set(CMAKE_CXX_FLAGS "-fno-exceptions ${CMAKE_CXX_FLAGS}") +endif() + +set(CMAKE_AUTOMOC TRUE) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +write_basic_package_version_file( + "${CMAKE_BINARY_DIR}/${LIBFM_QT_LIBRARY_NAME}-config-version.cmake" + VERSION ${LIBFM_QT_LIB_VERSION} + COMPATIBILITY AnyNewerVersion +) + +install(FILES + "${CMAKE_BINARY_DIR}/${LIBFM_QT_LIBRARY_NAME}-config-version.cmake" + DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/${LIBFM_QT_LIBRARY_NAME}" + COMPONENT Devel +) + +add_subdirectory(src) + +# add Doxygen support to generate API docs +# References: +# http://majewsky.wordpress.com/2010/08/14/tip-of-the-day-cmake-and-doxygen/ +# http://www.bluequartz.net/projects/EIM_Segmentation/SoftwareDocumentation/html/usewithcmakeproject.html +option(BUILD_DOCUMENTATION "Use Doxygen to create the HTML based API documentation" OFF) +if(BUILD_DOCUMENTATION) + find_package(Doxygen REQUIRED) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in" "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile" @ONLY) + add_custom_target(doc ALL + ${DOXYGEN_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile" + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + COMMENT "Generating API documentation with Doxygen" VERBATIM + ) + install(DIRECTORY + "${CMAKE_CURRENT_BINARY_DIR}/docs" + DESTINATION "${CMAKE_INSTALL_DOCDIR}" + COMPONENT Devel + ) +endif() + +# building tarball with CPack ------------------------------------------------- +# To create a source distribution, type: +# make package_source +include(InstallRequiredSystemLibraries) +set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") +set(CPACK_RESOURCE_FILE_README "${CMAKE_CURRENT_SOURCE_DIR}/README.md") +set(CPACK_PACKAGE_VENDOR "") +set(CPACK_PACKAGE_VERSION_MAJOR ${LIBFM_QT_VERSION_MAJOR}) +set(CPACK_PACKAGE_VERSION_MINOR ${LIBFM_QT_VERSION_MINOR}) +set(CPACK_PACKAGE_VERSION_PATCH ${LIBFM_QT_VERSION_PATCH}) +set(CPACK_GENERATOR TBZ2) +set(CPACK_SOURCE_GENERATOR TBZ2) +set(CPACK_SOURCE_IGNORE_FILES /build/;.gitignore;.*~;.git;.kdev4;temp) +include(CPack) diff --git a/Doxyfile.in b/Doxyfile.in new file mode 100644 index 0000000..b7961be --- /dev/null +++ b/Doxyfile.in @@ -0,0 +1,1890 @@ +# Doxyfile 1.8.4 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed +# in front of the TAG it is preceding . +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG + = value [value, ...] +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = "libfm-qt" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = @PROJECT_BINARY_DIR@/docs + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Latvian, Lithuanian, Norwegian, Macedonian, +# Persian, Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, +# Slovak, Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. Note that you specify absolute paths here, but also +# relative paths, which will be relative from the directory where doxygen is +# started. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name = value". +# For example adding "sideeffect = \par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name = value". For example adding +# "class = itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext = language, where ext is a file extension, +# and language is one of the parsers supported by doxygen: IDL, Java, +# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, +# C++. For instance to make doxygen treat .inc files as Fortran files (default +# is PHP), and .f files as C (default is Fortran), use: inc = Fortran f = C. Note +# that for custom extensions you also need to set FILE_PATTERNS otherwise the +# files are not read by doxygen. + +EXTENSION_MAPPING = + +# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all +# comments according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you +# can mix doxygen, HTML, and XML commands with Markdown formatting. +# Disable only in case of backward compatibilities issues. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by by putting a % sign in front of the word +# or globally by setting AUTOLINK_SUPPORT to NO. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES (the +# default) will make doxygen replace the get and set methods by a property in +# the documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields or simple typedef fields will be shown +# inline in the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO (the default), structs, classes, and unions are shown on a separate +# page (for HTML and Man pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can +# be an expensive process and often the same symbol appear multiple times in +# the code, doxygen keeps a cache of pre-resolved symbols. If the cache is too +# small doxygen will become slower. If the cache is too large, memory is wasted. +# The cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid +# range is 0..9, the default is 0, corresponding to a cache size of 2^16 = 65536 +# symbols. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST = YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if section-label ... \endif +# and \cond section-label ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. Do not use +# file names with spaces, bibtex cannot handle them. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = "@PROJECT_SOURCE_DIR@/src" + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = */*_p.h + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = LibFmQtData + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be ignored. +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern = filter (like *.cpp = my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext = (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C, C++ and Fortran comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If left blank doxygen will +# generate a default style sheet. Note that it is recommended to use +# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this +# tag will in the future become obsolete. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional +# user-defined cascading style sheet that is included after the standard +# style sheets created by doxygen. Using this option one can overrule +# certain style aspects. This is preferred over using HTML_STYLESHEET +# since it does not replace the standard style sheet and is therefor more +# robust against future updates. Doxygen will copy the style sheet file to +# the output directory. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of +# entries shown in the various tree structured indices initially; the user +# can expand and collapse entries dynamically later on. Doxygen will expand +# the tree to such a level that at most the specified number of entries are +# visible (unless a fully collapsed tree already exceeds this amount). +# So setting the number of entries 1 will produce a full collapsed tree by +# default. 0 is a special value representing an infinite number of entries +# and will result in a full expanded tree by default. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely +# identify the documentation publisher. This should be a reverse domain-name +# style string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you may also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and +# SVG. The default value is HTML-CSS, which is slower, but has the best +# compatibility. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to +# the MathJax Content Delivery Network so you can quickly see the result without +# installing MathJax. +# However, it is strongly recommended to install a local +# copy of MathJax from http://www.mathjax.org before deployment. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript +# pieces of code that will be used on startup of the MathJax code. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. +# There are two flavours of web server based search depending on the +# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for +# searching and an index file used by the script. When EXTERNAL_SEARCH is +# enabled the indexing and searching needs to be provided by external tools. +# See the manual for details. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain +# the search results. Doxygen ships with an example indexer (doxyindexer) and +# search engine (doxysearch.cgi) which are based on the open source search +# engine library Xapian. See the manual for configuration details. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will returned the search results when EXTERNAL_SEARCH is enabled. +# Doxygen ships with an example search engine (doxysearch) which is based on +# the open source search engine library Xapian. See the manual for configuration +# details. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id +# of to a relative location where the documentation can be found. +# The format is: EXTRA_SEARCH_MAPPINGS = id1 = loc1 id2 = loc2 ... + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4 will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images +# or other source files which should be copied to the LaTeX output directory. +# Note that the files will be copied as-is; there are no commands or markers +# available. + +LATEX_EXTRA_FILES = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options related to the DOCBOOK output +#--------------------------------------------------------------------------- + +# If the GENERATE_DOCBOOK tag is set to YES Doxygen will generate DOCBOOK files +# that can be used to generate PDF. + +GENERATE_DOCBOOK = NO + +# The DOCBOOK_OUTPUT tag is used to specify where the DOCBOOK pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in +# front of it. If left blank docbook will be used as the default path. + +DOCBOOK_OUTPUT = docbook + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name = definition (no spaces). If the definition and the = are +# omitted = 1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the : = operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. For each +# tag file the location of the external documentation should be added. The +# format of a tag file without this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1 = loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths +# or URLs. Note that each tag file must have a unique name (where the name does +# NOT include the path). If a tag file is not located in the directory in which +# doxygen is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# If the EXTERNAL_PAGES tag is set to YES all external pages will be listed +# in the related pages index. If set to NO, only the current project's +# pages will be listed. + +EXTERNAL_PAGES = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# manageable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 10 + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = YES + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..20fb9c7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,458 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS diff --git a/README.md b/README.md new file mode 100644 index 0000000..4340134 --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# libfm-qt + +Includes libfm-qt, the Qt port of the libfm - a library providing components to +build desktop file managers. + +Issue tracker: + https://github.com/lxde/pcmanfm-qt/issues + +LXQt website: + http://lxqt.org + +LXDE website: + http://lxde.org + + +# License + +libfm-qt is licensed under the terms of the +[LGPLv2.1](https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html) or any later version. + +See the LICENSE file for the full text of the license. diff --git a/cmake/LXQtTranslateDesktop.cmake b/cmake/LXQtTranslateDesktop.cmake new file mode 100644 index 0000000..522ed68 --- /dev/null +++ b/cmake/LXQtTranslateDesktop.cmake @@ -0,0 +1,107 @@ +#============================================================================= +# The lxqt_translate_desktop() function was copied from the the +# LXQt LxQtTranste.cmake +# +# Original Author: Alexander Sokolov +# +# funtion lxqt_translate_desktop(_RESULT +# SOURCES +# [TRANSLATION_DIR] translation_directory +# ) +# Output: +# _RESULT The generated .desktop (.desktop) files +# +# Input: +# +# SOURCES List of input desktop files (.destktop.in) to be translated +# (merged), relative to the CMakeList.txt. +# +# TRANSLATION_DIR Optional path to the directory with the .ts files, +# relative to the CMakeList.txt. Defaults to +# "translations". +# +#============================================================================= + +function(lxqt_translate_desktop _RESULT) + # Parse arguments *************************************** + set(oneValueArgs TRANSLATION_DIR) + set(multiValueArgs SOURCES) + + cmake_parse_arguments(_ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + # check for unknown arguments + set(_UNPARSED_ARGS ${_ARGS_UNPARSED_ARGUMENTS}) + if (NOT ${_UNPARSED_ARGS} STREQUAL "") + MESSAGE(FATAL_ERROR + "Unknown arguments '${_UNPARSED_ARGS}'.\n" + "See lxqt_translate_desktop() documenation for more information.\n" + ) + endif() + + if (NOT DEFINED _ARGS_SOURCES) + set(${_RESULT} "" PARENT_SCOPE) + return() + else() + set(_sources ${_ARGS_SOURCES}) + endif() + + if (NOT DEFINED _ARGS_TRANSLATION_DIR) + set(_translationDir "translations") + else() + set(_translationDir ${_ARGS_TRANSLATION_DIR}) + endif() + + + get_filename_component (_translationDir ${_translationDir} ABSOLUTE) + + foreach (_inFile ${_sources}) + get_filename_component(_inFile ${_inFile} ABSOLUTE) + get_filename_component(_fileName ${_inFile} NAME_WE) + #Extract the real extension ............ + get_filename_component(_fileExt ${_inFile} EXT) + string(REPLACE ".in" "" _fileExt ${_fileExt}) + #....................................... + set(_outFile "${CMAKE_CURRENT_BINARY_DIR}/${_fileName}${_fileExt}") + + file(GLOB _translations + ${_translationDir}/${_fileName}_*${_fileExt} + ${_translationDir}/local/${_fileName}_*${_fileExt} + ) + + set(_pattern "'\\[.*]\\s*='") + if (_translations) + add_custom_command(OUTPUT ${_outFile} + COMMAND grep -v "'#TRANSLATIONS_DIR='" ${_inFile} > ${_outFile} + COMMAND grep -h ${_pattern} ${_translations} >> ${_outFile} + COMMENT "Generating ${_fileName}${_fileExt}" + ) + else() + add_custom_command(OUTPUT ${_outFile} + COMMAND grep -v "'#TRANSLATIONS_DIR='" ${_inFile} > ${_outFile} + COMMENT "Generating ${_fileName}${_fileExt}" + ) + endif() + + set(__result ${__result} ${_outFile}) + + + # TX file *********************************************** + set(_txFile "${CMAKE_BINARY_DIR}/tx/${_fileName}${_fileExt}.tx.sh") + string(REPLACE "${CMAKE_SOURCE_DIR}/" "" _tx_translationDir ${_translationDir}) + string(REPLACE "${CMAKE_SOURCE_DIR}/" "" _tx_inFile ${_inFile}) + string(REPLACE "." "" _fileType ${_fileExt}) + + file(WRITE ${_txFile} + "[ -f ${_inFile} ] || exit 0\n" + "echo '[lxde-qt.${_fileName}_${_fileType}]'\n" + "echo 'type = DESKTOP'\n" + "echo 'source_lang = en'\n" + "echo 'source_file = ${_tx_inFile}'\n" + "echo 'file_filter = ${_tx_translationDir}/${_fileName}_${_fileExt}'\n" + "echo ''\n" + ) + + endforeach() + + set(${_RESULT} ${__result} PARENT_SCOPE) +endfunction(lxqt_translate_desktop) diff --git a/cmake/LXQtTranslateTs.cmake b/cmake/LXQtTranslateTs.cmake new file mode 100644 index 0000000..5998830 --- /dev/null +++ b/cmake/LXQtTranslateTs.cmake @@ -0,0 +1,131 @@ +#============================================================================= +# Copyright 2014 Luís Pereira +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= +# +# funtion lxqt_translate_ts(qmFiles +# [USE_QT5 [Yes | No]] +# [UPDATE_TRANSLATIONS [Yes | No]] +# SOURCES +# [TEMPLATE] translation_template +# [TRANSLATION_DIR] translation_directory +# [INSTALL_DIR] install_directory +# ) +# Output: +# qmFiles The generated compiled translations (.qm) files +# +# Input: +# USE_QT5 Optional flag to choose between Qt4 and Qt5. Defaults to Qt5 +# +# UPDATE_TRANSLATIONS Optional flag. Setting it to Yes, extracts and +# compiles the translations. Setting it No, only +# compiles them. +# +# TEMPLATE Optional translations files base name. Defaults to +# ${PROJECT_NAME}. An .ts extensions is added. +# +# TRANSLATION_DIR Optional path to the directory with the .ts files, +# relative to the CMakeList.txt. Defaults to +# "translations". +# +# INSTALL_DIR Optional destination of the file compiled files (qmFiles). +# If not present no installation is performed + + + +# CMake v2.8.3 needed to use the CMakeParseArguments module +cmake_minimum_required(VERSION 2.8.3 FATAL_ERROR) + +# We use our patched version to round a annoying bug. +include(Qt5PatchedLinguistToolsMacros) + +function(lxqt_translate_ts qmFiles) + set(oneValueArgs USE_QT5 UPDATE_TRANSLATIONS TEMPLATE TRANSLATION_DIR INSTALL_DIR) + set(multiValueArgs SOURCES) + cmake_parse_arguments(TR "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if (NOT DEFINED TR_UPDATE_TRANSLATIONS) + set(TR_UPDATE_TRANSLATIONS "No") + endif() + + if (NOT DEFINED TR_USE_QT5) + set(TR_USE_QT5 "Yes") + endif() + + if(NOT DEFINED TR_TEMPLATE) + set(TR_TEMPLATE "${PROJECT_NAME}") + endif() + + if (NOT DEFINED TR_TRANSLATION_DIR) + set(TR_TRANSLATION_DIR "translations") + endif() + + file(GLOB tsFiles "${TR_TRANSLATION_DIR}/${TR_TEMPLATE}_*.ts") + set(templateFile "${TR_TRANSLATION_DIR}/${TR_TEMPLATE}.ts") + + if(TR_USE_QT5) + # Qt5 + if (TR_UPDATE_TRANSLATIONS) + qt5_patched_create_translation(QMS + ${TR_SOURCES} + ${templateFile} + OPTIONS -locations absolute + ) + qt5_patched_create_translation(QM + ${TR_SOURCES} + ${tsFiles} + OPTIONS -locations absolute + ) + else() + qt5_patched_add_translation(QM ${tsFiles}) + endif() + else() + # Qt4 + if(TR_UPDATE_TRANSLATIONS) + qt4_create_translation(QMS + ${TR_SOURCES} + ${templateFile} + OPTIONS -locations absolute + ) + qt4_create_translation(QM + ${TR_SOURCES} + ${tsFiles} + OPTIONS -locations absolute + ) + else() + qt4_add_translation(QM ${tsFiles}) + endif() + endif() + + if(TR_UPDATE_TRANSLATIONS) + add_custom_target("update_${TR_TEMPLATE}_ts" ALL DEPENDS ${QMS}) + endif() + + if(DEFINED TR_INSTALL_DIR) + install(FILES ${QM} DESTINATION ${TR_INSTALL_DIR}) + endif() + + set(${qmFiles} ${QM} PARENT_SCOPE) +endfunction() diff --git a/cmake/Qt5PatchedLinguistToolsMacros.cmake b/cmake/Qt5PatchedLinguistToolsMacros.cmake new file mode 100644 index 0000000..e5c2f5c --- /dev/null +++ b/cmake/Qt5PatchedLinguistToolsMacros.cmake @@ -0,0 +1,112 @@ +#============================================================================= +# Copyright 2005-2011 Kitware, Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * Neither the name of Kitware, Inc. nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +include(CMakeParseArguments) + +function(QT5_PATCHED_CREATE_TRANSLATION _qm_files) + set(options) + set(oneValueArgs) + set(multiValueArgs OPTIONS) + + cmake_parse_arguments(_LUPDATE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + set(_lupdate_files ${_LUPDATE_UNPARSED_ARGUMENTS}) + set(_lupdate_options ${_LUPDATE_OPTIONS}) + + set(_my_sources) + set(_my_tsfiles) + foreach(_file ${_lupdate_files}) + get_filename_component(_ext ${_file} EXT) + get_filename_component(_abs_FILE ${_file} ABSOLUTE) + if(_ext MATCHES "ts") + list(APPEND _my_tsfiles ${_abs_FILE}) + else() + list(APPEND _my_sources ${_abs_FILE}) + endif() + endforeach() + foreach(_ts_file ${_my_tsfiles}) + if(_my_sources) + # make a list file to call lupdate on, so we don't make our commands too + # long for some systems +# get_filename_component(_ts_name ${_ts_file} NAME_WE) + + get_filename_component(_name ${_ts_file} NAME) + string(REGEX REPLACE "^(.+)(\\.[^.]+)$" "\\1" _ts_name ${_name}) + + set(_ts_lst_file "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/${_ts_name}_lst_file") + set(_lst_file_srcs) + foreach(_lst_file_src ${_my_sources}) + set(_lst_file_srcs "${_lst_file_src}\n${_lst_file_srcs}") + endforeach() + + get_directory_property(_inc_DIRS INCLUDE_DIRECTORIES) + foreach(_pro_include ${_inc_DIRS}) + get_filename_component(_abs_include "${_pro_include}" ABSOLUTE) + set(_lst_file_srcs "-I${_pro_include}\n${_lst_file_srcs}") + endforeach() + + file(WRITE ${_ts_lst_file} "${_lst_file_srcs}") + endif() + add_custom_command(OUTPUT ${_ts_file} + COMMAND ${Qt5_LUPDATE_EXECUTABLE} + ARGS ${_lupdate_options} "@${_ts_lst_file}" -ts ${_ts_file} + DEPENDS ${_my_sources} ${_ts_lst_file} VERBATIM) + endforeach() + qt5_patched_add_translation(${_qm_files} ${_my_tsfiles}) + set(${_qm_files} ${${_qm_files}} PARENT_SCOPE) +endfunction() + + +function(QT5_PATCHED_ADD_TRANSLATION _qm_files) + foreach(_current_FILE ${ARGN}) + get_filename_component(_abs_FILE ${_current_FILE} ABSOLUTE) +# get_filename_component(qm ${_abs_FILE} NAME_WE) + + get_filename_component(_name ${_abs_FILE} NAME) + string(REGEX REPLACE "^(.+)(\\.[^.]+)$" "\\1" qm ${_name}) + + get_source_file_property(output_location ${_abs_FILE} OUTPUT_LOCATION) + if(output_location) + file(MAKE_DIRECTORY "${output_location}") + set(qm "${output_location}/${qm}.qm") + else() + set(qm "${CMAKE_CURRENT_BINARY_DIR}/${qm}.qm") + endif() + + add_custom_command(OUTPUT ${qm} + COMMAND ${Qt5_LRELEASE_EXECUTABLE} + ARGS ${_abs_FILE} -qm ${qm} + DEPENDS ${_abs_FILE} VERBATIM + ) + list(APPEND ${_qm_files} ${qm}) + endforeach() + set(${_qm_files} ${${_qm_files}} PARENT_SCOPE) +endfunction() diff --git a/cmake/fm-qt-config.cmake.in b/cmake/fm-qt-config.cmake.in new file mode 100644 index 0000000..5dc67fd --- /dev/null +++ b/cmake/fm-qt-config.cmake.in @@ -0,0 +1,63 @@ +#============================================================================= +# Copyright 2015 Luís Pereira +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#============================================================================= + +@PACKAGE_INIT@ + +if (CMAKE_VERSION VERSION_LESS 3.0.2) + message(FATAL_ERROR \"fm-qt requires at least CMake version 3.0.2\") +endif() + +include(CMakeFindDependencyMacro) + +find_dependency(Qt5Widgets "@REQUIRED_QT_VERSION@") +find_dependency(Qt5X11Extras "@REQUIRED_QT_VERSION@") + +find_package(PkgConfig REQUIRED) +pkg_check_modules(PKG_GLIB glib-2.0) +pkg_check_modules(PKG_GIO + gio-2.0 + gio-unix-2.0 +) + +pkg_check_modules(PKG_FM REQUIRED libfm>=@REQUIRED_LIBFM_VERSION@) +pkg_check_modules(PKG_MENUCACHE REQUIRED libmenu-cache>=@REQUIRED_LIBMENUCACHE_VERSION@) + +if (NOT TARGET @LIBFM_QT_LIBRARY_NAME@) + include("${CMAKE_CURRENT_LIST_DIR}/@LIBFM_QT_LIBRARY_NAME@-targets.cmake") + set_property(TARGET "@LIBFM_QT_LIBRARY_NAME@" APPEND PROPERTY + INTERFACE_INCLUDE_DIRECTORIES + "${PKG_GLIB_INCLUDE_DIRS}" + "${PKG_GIO_INCLUDE_DIRS}" + "${PKG_FM_INCLUDE_DIRS}" + "${PKG_MENUCACHE_INCLUDE_DIRS}" + ) + set_property(TARGET "@LIBFM_QT_LIBRARY_NAME@" APPEND PROPERTY + INTERFACE_COMPILE_DEFINITIONS + "QT_NO_KEYWORDS" + "LIBFM_DATA_DIR=\"${PKG_FM_PREFIX}/share/libfm\"" + ) +endif() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..1264952 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,195 @@ +set(libfm_TRANSLATION_TEMPLATE ${PROJECT_NAME}) + +include_directories( + "${LIBFM_INCLUDE_DIRS}" + "${LIBFM_INCLUDEDIR}/libfm" # to workaround incorrect #include in fm-actions. + "${LIBMENUCACHE_INCLUDE_DIRS}" + "${SYSTEM_LIBS_INCLUDE_DIRS}" +) + +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(${LIBFM_QT_LIBRARY_NAME} SHARED + ${libfm_SRCS} + ${libfm_UIS_H} + ${QM_FILES} +) + +set_property( + TARGET ${LIBFM_QT_LIBRARY_NAME} APPEND + PROPERTY COMPILE_DEFINITIONS + 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 ${LIBFM_QT_LIBRARY_NAME} APPEND PROPERTY COMPILE_DEFINITIONS CUSTOM_ACTIONS) +endif() + +install(EXPORT + "${LIBFM_QT_LIBRARY_NAME}-targets" + DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/${LIBFM_QT_LIBRARY_NAME}" + COMPONENT Devel +) + +target_link_libraries(${LIBFM_QT_LIBRARY_NAME} + Qt5::Widgets + Qt5::X11Extras + ${LIBFM_LIBRARIES} + ${LIBMENUCACHE_LIBRARIES} + ${SYSTEM_LIBS_LIBRARIES} +) + +# set libtool soname +set_target_properties(${LIBFM_QT_LIBRARY_NAME} PROPERTIES + VERSION ${LIBFM_QT_LIB_VERSION} + SOVERSION ${LIBFM_QT_LIB_SOVERSION} +) + +target_include_directories(${LIBFM_QT_LIBRARY_NAME} + INTERFACE "$" +) + +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/${LIBFM_QT_LIBRARY_NAME}_export.h" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/libfm-qt" + COMPONENT Devel +) + +# 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}/libfm-qt" + COMPONENT Devel + FILES_MATCHING PATTERN "*.h" +) + +generate_export_header(${LIBFM_QT_LIBRARY_NAME} + EXPORT_MACRO_NAME LIBFM_QT_API +) + +configure_package_config_file( + "${PROJECT_SOURCE_DIR}/cmake/fm-qt-config.cmake.in" + "${PROJECT_BINARY_DIR}/install/${LIBFM_QT_LIBRARY_NAME}-config.cmake" + INSTALL_DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/${LIBFM_QT_LIBRARY_NAME}" +) + +install(FILES + "${PROJECT_BINARY_DIR}/install/${LIBFM_QT_LIBRARY_NAME}-config.cmake" + DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/${LIBFM_QT_LIBRARY_NAME}" + COMPONENT Devel +) + +# FIXME: add libtool version to the lib (soname) later. +# FIXME: only export public symbols + +install(TARGETS ${LIBFM_QT_LIBRARY_NAME} + DESTINATION "${CMAKE_INSTALL_LIBDIR}" + EXPORT "${LIBFM_QT_LIBRARY_NAME}-targets" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + PUBLIC_HEADER + COMPONENT Runtime +) + +export(TARGETS ${LIBFM_QT_LIBRARY_NAME} + FILE "${CMAKE_BINARY_DIR}/${LIBFM_QT_LIBRARY_NAME}-targets.cmake" + EXPORT_LINK_INTERFACE_LIBRARIES +) + +# install a pkgconfig file for libfm-qt +set(REQUIRED_QT "Qt5Widgets >= ${REQUIRED_QT_VERSION} Qt5X11Extras >= ${REQUIRED_QT_VERSION}") +configure_file(libfm-qt.pc.in lib${LIBFM_QT_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${LIBFM_QT_LIBRARY_NAME}.pc" + DESTINATION libdata/pkgconfig + COMPONENT Devel + ) +else() + install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/lib${LIBFM_QT_LIBRARY_NAME}.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" + COMPONENT Devel + ) +endif() + +install(FILES + ${QM_FILES} + DESTINATION "${CMAKE_INSTALL_DATADIR}/libfm-qt/translations" + COMPONENT Runtime +) + +# prevent the generated files from being deleted during make cleaner +set_directory_properties(PROPERTIES CLEAN_NO_CUSTOM true) diff --git a/src/app-chooser-dialog.ui b/src/app-chooser-dialog.ui new file mode 100644 index 0000000..ef1ebee --- /dev/null +++ b/src/app-chooser-dialog.ui @@ -0,0 +1,183 @@ + + + AppChooserDialog + + + + 0 + 0 + 432 + 387 + + + + Choose an Application + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + + + + + 0 + 1 + + + + 0 + + + + Installed Applications + + + + + + + + + + Custom Command + + + + + + Command line to execute: + + + + + + + + + + Application name: + + + + + + + + + + <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> + + + Qt::RichText + + + + + + + false + + + Keep terminal window open after command execution + + + + + + + Execute in terminal emulator + + + + + + + + + + + Set selected application as default action of this file type + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + Fm::AppMenuView + QTreeView +
appmenuview.h
+
+
+ + + + buttonBox + accepted() + AppChooserDialog + accept() + + + 227 + 359 + + + 157 + 274 + + + + + buttonBox + rejected() + AppChooserDialog + reject() + + + 295 + 365 + + + 286 + 274 + + + + + useTerminal + toggled(bool) + keepTermOpen + setEnabled(bool) + + + 72 + 260 + + + 79 + 282 + + + + +
diff --git a/src/appchoosercombobox.cpp b/src/appchoosercombobox.cpp new file mode 100644 index 0000000..5420930 --- /dev/null +++ b/src/appchoosercombobox.cpp @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "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(&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(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 diff --git a/src/appchoosercombobox.h b/src/appchoosercombobox.h new file mode 100644 index 0000000..53ffc3e --- /dev/null +++ b/src/appchoosercombobox.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef FM_APPCHOOSERCOMBOBOX_H +#define FM_APPCHOOSERCOMBOBOX_H + +#include "libfmqtglobals.h" +#include +#include + +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 diff --git a/src/appchooserdialog.cpp b/src/appchooserdialog.cpp new file mode 100644 index 0000000..0a2b19d --- /dev/null +++ b/src/appchooserdialog.cpp @@ -0,0 +1,286 @@ +/* + * Copyright 2010-2014 Hong Jen Yee (PCMan) + * Copyright 2012-2013 Andriy Grytsenko (LStranger) + * + * 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 +#include + +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 diff --git a/src/appchooserdialog.h b/src/appchooserdialog.h new file mode 100644 index 0000000..64a5464 --- /dev/null +++ b/src/appchooserdialog.h @@ -0,0 +1,74 @@ +/* + * Copyright 2010-2014 Hong Jen Yee (PCMan) + * Copyright 2012-2013 Andriy Grytsenko (LStranger) + * + * 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 +#include "libfmqtglobals.h" +#include + +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 diff --git a/src/applaunchcontext.cpp b/src/applaunchcontext.cpp new file mode 100644 index 0000000..727f462 --- /dev/null +++ b/src/applaunchcontext.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "applaunchcontext.h" +#include +#include + +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; +} diff --git a/src/applaunchcontext.h b/src/applaunchcontext.h new file mode 100644 index 0000000..606c3b6 --- /dev/null +++ b/src/applaunchcontext.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_APP_LAUNCHCONTEXT_H +#define FM_APP_LAUNCHCONTEXT_H + +#include "libfmqtglobals.h" +#include +#include + +#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 diff --git a/src/appmenuview.cpp b/src/appmenuview.cpp new file mode 100644 index 0000000..6804120 --- /dev/null +++ b/src/appmenuview.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "appmenuview.h" +#include +#include "icontheme.h" +#include "appmenuview_p.h" +#include + +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(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 diff --git a/src/appmenuview.h b/src/appmenuview.h new file mode 100644 index 0000000..99c5607 --- /dev/null +++ b/src/appmenuview.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef FM_APPMENUVIEW_H +#define FM_APPMENUVIEW_H + +#include +#include "libfmqtglobals.h" +#include +#include + +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(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 diff --git a/src/appmenuview_p.h b/src/appmenuview_p.h new file mode 100644 index 0000000..03730d0 --- /dev/null +++ b/src/appmenuview_p.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef FM_APPMENUVIEW_P_H +#define FM_APPMENUVIEW_P_H + +#include +#include +#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 = nullptr; + 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 diff --git a/src/bookmarkaction.cpp b/src/bookmarkaction.cpp new file mode 100644 index 0000000..e67b6ac --- /dev/null +++ b/src/bookmarkaction.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "bookmarkaction.h" + +namespace Fm { + +BookmarkAction::BookmarkAction(FmBookmarkItem* item, QObject* parent): + QAction(parent), + item_(fm_bookmark_item_ref(item)) { + + setText(QString::fromUtf8(item->name)); +} + +} // namespace Fm diff --git a/src/bookmarkaction.h b/src/bookmarkaction.h new file mode 100644 index 0000000..65c5ebd --- /dev/null +++ b/src/bookmarkaction.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef BOOKMARKACTION_H +#define BOOKMARKACTION_H + +#include "libfmqtglobals.h" +#include +#include + +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 diff --git a/src/browsehistory.cpp b/src/browsehistory.cpp new file mode 100644 index 0000000..f33d185 --- /dev/null +++ b/src/browsehistory.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "browsehistory.h" + +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 + } +} + + +} // namespace Fm diff --git a/src/browsehistory.h b/src/browsehistory.h new file mode 100644 index 0000000..27864f3 --- /dev/null +++ b/src/browsehistory.h @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_BROWSEHISTORY_H +#define FM_BROWSEHISTORY_H + +#include "libfmqtglobals.h" +#include +#include + +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 { + +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 diff --git a/src/cachedfoldermodel.cpp b/src/cachedfoldermodel.cpp new file mode 100644 index 0000000..07b1e95 --- /dev/null +++ b/src/cachedfoldermodel.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "cachedfoldermodel.h" + +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(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(); + } +} + + +} // namespace Fm diff --git a/src/cachedfoldermodel.h b/src/cachedfoldermodel.h new file mode 100644 index 0000000..273fba9 --- /dev/null +++ b/src/cachedfoldermodel.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_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 diff --git a/src/colorbutton.cpp b/src/colorbutton.cpp new file mode 100644 index 0000000..346abcd --- /dev/null +++ b/src/colorbutton.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "colorbutton.h" +#include + +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(); + } +} + + +} // namespace Fm diff --git a/src/colorbutton.h b/src/colorbutton.h new file mode 100644 index 0000000..e8e0072 --- /dev/null +++ b/src/colorbutton.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_COLORBUTTON_H +#define FM_COLORBUTTON_H + +#include "libfmqtglobals.h" +#include +#include + +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 diff --git a/src/createnewmenu.cpp b/src/createnewmenu.cpp new file mode 100644 index 0000000..1597deb --- /dev/null +++ b/src/createnewmenu.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#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(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 diff --git a/src/createnewmenu.h b/src/createnewmenu.h new file mode 100644 index 0000000..09ca9d9 --- /dev/null +++ b/src/createnewmenu.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef FM_CREATENEWMENU_H +#define FM_CREATENEWMENU_H + +#include "libfmqtglobals.h" +#include +#include + +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 diff --git a/src/dirtreemodel.cpp b/src/dirtreemodel.cpp new file mode 100644 index 0000000..6947327 --- /dev/null +++ b/src/dirtreemodel.cpp @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "dirtreemodel.h" +#include "dirtreemodelitem.h" +#include + +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& 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& 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(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 diff --git a/src/dirtreemodel.h b/src/dirtreemodel.h new file mode 100644 index 0000000..c82ff3b --- /dev/null +++ b/src/dirtreemodel.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef FM_DIRTREEMODEL_H +#define FM_DIRTREEMODEL_H + +#include "libfmqtglobals.h" +#include +#include +#include +#include +#include +#include + +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 rootItems_; +}; +} + +#endif // FM_DIRTREEMODEL_H diff --git a/src/dirtreemodelitem.cpp b/src/dirtreemodelitem.cpp new file mode 100644 index 0000000..05b6486 --- /dev/null +++ b/src/dirtreemodelitem.cpp @@ -0,0 +1,337 @@ +/* + * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "dirtreemodelitem.h" +#include "dirtreemodel.h" +#include "icontheme.h" +#include + +namespace Fm { + +DirTreeModelItem::DirTreeModelItem(): + model_(nullptr), + folder_(nullptr), + expanded_(false), + loaded_(false), + fileInfo_(nullptr), + placeHolderChild_(nullptr), + parent_(nullptr) { +} + +DirTreeModelItem::DirTreeModelItem(FmFileInfo* info, DirTreeModel* model, DirTreeModelItem* parent): + model_(model), + folder_(nullptr), + 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_(nullptr), + 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_ = nullptr; + } +} + +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::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(""); + QModelIndex placeHolderIndex = _this->placeHolderChild_->index(); + // qDebug() << "placeHolderIndex: "<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_ = nullptr; + } + + 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; + for (const auto item : children_) { + if(item->fileInfo_ && strcmp(fm_file_info_get_name(item->fileInfo_), utf8_name) == 0) { + if(pos) + *pos = i; + return item; + } + ++i; + } + return nullptr; +} + +DirTreeModelItem* DirTreeModelItem::childFromPath(FmPath* path, bool recursive) const { + Q_ASSERT(path != nullptr); + + 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 nullptr; +} + +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::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 + diff --git a/src/dirtreemodelitem.h b/src/dirtreemodelitem.h new file mode 100644 index 0000000..4d77ffb --- /dev/null +++ b/src/dirtreemodelitem.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef FM_DIRTREEMODELITEM_H +#define FM_DIRTREEMODELITEM_H + +#include "libfmqtglobals.h" +#include +#include +#include +#include + +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 = nullptr); + ~DirTreeModelItem(); + + void loadFolder(); + void unloadFolder(); + + inline bool isPlaceHolder() const { + return (fileInfo_ == nullptr); + } + + 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 children_; + QList hiddenChildren_; + DirTreeModel* model_; +}; + +} + +#endif // FM_DIRTREEMODELITEM_H diff --git a/src/dirtreeview.cpp b/src/dirtreeview.cpp new file mode 100644 index 0000000..de7e589 --- /dev/null +++ b/src/dirtreeview.cpp @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "dirtreeview.h" +#include +#include +#include +#include +#include +#include "dirtreemodel.h" +#include "dirtreemodelitem.h" +#include "filemenu.h" + +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(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(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(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(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(data.value()); + 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(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(sender())) { + setCurrentIndex(action->data().toModelIndex()); + } +} + +void DirTreeView::onNewWindow() { + if(QAction* action = qobject_cast(sender())) { + FmPath* path = reinterpret_cast(action->data().value()); + Q_EMIT openFolderInNewWindowRequested(path); + } +} + +void DirTreeView::onNewTab() { + if(QAction* action = qobject_cast(sender())) { + FmPath* path = reinterpret_cast(action->data().value()); + Q_EMIT openFolderInNewTabRequested(path); + } +} + +void DirTreeView::onOpenInTerminal() { + if(QAction* action = qobject_cast(sender())) { + FmPath* path = reinterpret_cast(action->data().value()); + Q_EMIT openFolderInTerminalRequested(path); + } +} + +void DirTreeView::onNewFolder() { + if(QAction* action = qobject_cast(sender())) { + FmPath* path = reinterpret_cast(action->data().value()); + Q_EMIT createNewFolderRequested(path); + } +} + +void DirTreeView::onCollapsed(const QModelIndex& index) { + DirTreeModel* treeModel = static_cast(model()); + if(treeModel) { + treeModel->unloadRow(index); + } +} + +void DirTreeView::onExpanded(const QModelIndex& index) { + DirTreeModel* treeModel = static_cast(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(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); + } +} + + +} // namespace Fm diff --git a/src/dirtreeview.h b/src/dirtreeview.h new file mode 100644 index 0000000..44f4981 --- /dev/null +++ b/src/dirtreeview.h @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef FM_DIRTREEVIEW_H +#define FM_DIRTREEVIEW_H + +#include "libfmqtglobals.h" +#include +#include +#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 pathsToExpand_; + DirTreeModelItem* currentExpandingItem_; +}; + +} + +#endif // FM_DIRTREEVIEW_H diff --git a/src/dndactionmenu.cpp b/src/dndactionmenu.cpp new file mode 100644 index 0000000..73625ee --- /dev/null +++ b/src/dndactionmenu.cpp @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "dndactionmenu.h" + +namespace Fm { + +DndActionMenu::DndActionMenu(Qt::DropActions possibleActions, QWidget* parent) + : QMenu(parent) + , copyAction(nullptr) + , moveAction(nullptr) + , linkAction(nullptr) + , cancelAction(nullptr) +{ + if (possibleActions.testFlag(Qt::CopyAction)) + copyAction = addAction(QIcon::fromTheme("edit-copy"), tr("Copy here")); + if (possibleActions.testFlag(Qt::MoveAction)) + moveAction = addAction(tr("Move here")); + if (possibleActions.testFlag(Qt::LinkAction)) + linkAction = addAction(tr("Create symlink here")); + addSeparator(); + cancelAction = addAction(tr("Cancel")); +} + +DndActionMenu::~DndActionMenu() { + +} + +Qt::DropAction DndActionMenu::askUser(Qt::DropActions possibleActions, QPoint pos) { + Qt::DropAction result = Qt::IgnoreAction; + DndActionMenu menu{possibleActions}; + QAction* action = menu.exec(pos); + if (nullptr != action) + { + if(action == menu.copyAction) + result = Qt::CopyAction; + else if(action == menu.moveAction) + result = Qt::MoveAction; + else if(action == menu.linkAction) + result = Qt::LinkAction; + } + return result; +} + + +} // namespace Fm diff --git a/src/dndactionmenu.h b/src/dndactionmenu.h new file mode 100644 index 0000000..247aa1f --- /dev/null +++ b/src/dndactionmenu.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_DNDACTIONMENU_H +#define FM_DNDACTIONMENU_H + +#include "libfmqtglobals.h" +#include +#include + +namespace Fm { + +class DndActionMenu : public QMenu { +Q_OBJECT +public: + explicit DndActionMenu(Qt::DropActions possibleActions, QWidget* parent = 0); + virtual ~DndActionMenu(); + + static Qt::DropAction askUser(Qt::DropActions possibleActions, QPoint pos); + +private: + QAction* copyAction; + QAction* moveAction; + QAction* linkAction; + QAction* cancelAction; +}; + +} + +#endif // FM_DNDACTIONMENU_H diff --git a/src/dnddest.cpp b/src/dnddest.cpp new file mode 100644 index 0000000..71cabb3 --- /dev/null +++ b/src/dnddest.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "dnddest.h" +#include "fileoperation.h" +#include "utilities.h" + +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; +} + + +} // namespace Fm diff --git a/src/dnddest.h b/src/dnddest.h new file mode 100644 index 0000000..e292c3f --- /dev/null +++ b/src/dnddest.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef FM_DNDDEST_H +#define FM_DNDDEST_H + +#include +#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 diff --git a/src/edit-bookmarks.ui b/src/edit-bookmarks.ui new file mode 100644 index 0000000..8d989e6 --- /dev/null +++ b/src/edit-bookmarks.ui @@ -0,0 +1,143 @@ + + + EditBookmarksDialog + + + + 0 + 0 + 480 + 320 + + + + Edit Bookmarks + + + + + + true + + + true + + + QAbstractItemView::InternalMove + + + Qt::MoveAction + + + false + + + false + + + 100 + + + + Name + + + + + Location + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + &Add Item + + + + + + + + + + &Remove Item + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Use drag and drop to reorder the items + + + + + + + + + buttonBox + accepted() + EditBookmarksDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + EditBookmarksDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/editbookmarksdialog.cpp b/src/editbookmarksdialog.cpp new file mode 100644 index 0000000..3fae409 --- /dev/null +++ b/src/editbookmarksdialog.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "editbookmarksdialog.h" +#include "ui_edit-bookmarks.h" +#include +#include +#include +#include +#include + +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(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 sels = ui->treeWidget->selectedItems(); + Q_FOREACH(QTreeWidgetItem* item, sels) { + delete item; + } +} + + +} // namespace Fm diff --git a/src/editbookmarksdialog.h b/src/editbookmarksdialog.h new file mode 100644 index 0000000..bf76f9b --- /dev/null +++ b/src/editbookmarksdialog.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_EDITBOOKMARKSDIALOG_H +#define FM_EDITBOOKMARKSDIALOG_H + +#include "libfmqtglobals.h" +#include +#include + +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 diff --git a/src/exec-file.ui b/src/exec-file.ui new file mode 100644 index 0000000..c5a9ea3 --- /dev/null +++ b/src/exec-file.ui @@ -0,0 +1,163 @@ + + + ExecFileDialog + + + + 0 + 0 + 487 + 58 + + + + Execute file + + + + + + + + + + + + + + true + + + + + + + + + + + &Open + + + + + + true + + + + + + + E&xecute + + + + + + + + + + Execute in &Terminal + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Cancel + + + + + + + + + + + + + + cancel + clicked() + ExecFileDialog + reject() + + + 341 + 39 + + + 196 + 28 + + + + + exec + clicked() + ExecFileDialog + accept() + + + 56 + 39 + + + 196 + 28 + + + + + execTerm + clicked() + ExecFileDialog + accept() + + + 201 + 39 + + + 196 + 28 + + + + + open + clicked() + ExecFileDialog + accept() + + + 346 + 39 + + + 250 + 28 + + + + + diff --git a/src/execfiledialog.cpp b/src/execfiledialog.cpp new file mode 100644 index 0000000..dcf420a --- /dev/null +++ b/src/execfiledialog.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "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 diff --git a/src/execfiledialog_p.h b/src/execfiledialog_p.h new file mode 100644 index 0000000..10af394 --- /dev/null +++ b/src/execfiledialog_p.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef FM_EXECFILEDIALOG_H +#define FM_EXECFILEDIALOG_H + +#include +#include + +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 diff --git a/src/file-operation-dialog.ui b/src/file-operation-dialog.ui new file mode 100644 index 0000000..f81018e --- /dev/null +++ b/src/file-operation-dialog.ui @@ -0,0 +1,171 @@ + + + FileOperationDialog + + + + 0 + 0 + 450 + 246 + + + + + + + + + + + + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Destination: + + + + + + + + 0 + 0 + + + + + + + true + + + + + + + Processing: + + + + + + + + 0 + 0 + + + + Preparing... + + + + + + + Progress + + + + + + + Qt::AlignCenter + + + + + + + + 0 + 0 + + + + Time remaining: + + + + + + + + 0 + 0 + + + + + + + + + + + + 0 + 0 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel + + + + + + + + + buttonBox + accepted() + FileOperationDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + FileOperationDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/file-props.ui b/src/file-props.ui new file mode 100644 index 0000000..c0b3918 --- /dev/null +++ b/src/file-props.ui @@ -0,0 +1,736 @@ + + + FilePropsDialog + + + + 0 + 0 + 424 + 456 + + + + File Properties + + + + + + + + + 10 + + + 10 + + + 10 + + + 10 + + + + + 0 + + + + General + + + + 12 + + + 6 + + + + + + 0 + 0 + + + + + + + + + + + + + 32 + 32 + + + + + + + + + + + Location: + + + + + + + + 0 + 0 + + + + + + + true + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + File type: + + + + + + + + 0 + 0 + + + + + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Mime type: + + + + + + + + 0 + 0 + + + + + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + File size: + + + + + + + + 0 + 0 + + + + + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + On-disk size: + + + + + + + + 0 + 0 + + + + + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Last modified: + + + + + + + + 0 + 0 + + + + + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Link target: + + + + + + + + 0 + 0 + + + + + + + true + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Open With: + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + 0 + 0 + + + + + + + + Last accessed: + + + + + + + + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + Permissions + + + + 6 + + + + + + 0 + 0 + + + + Ownership + + + + 12 + + + 6 + + + + + + + + + + + + 0 + 0 + + + + Group: + + + + + + + + 0 + 0 + + + + Owner: + + + + + + + + + + + 0 + 0 + + + + Access Control + + + + + + + 0 + 0 + + + + 0 + + + + + + + Owner: + + + + + + + + 0 + 0 + + + + + + + + Group: + + + + + + + + 0 + 0 + + + + + + + + Other: + + + + + + + + 0 + 0 + + + + + + + + Make the file executable + + + true + + + + + + + + + + + 0 + + + 6 + + + + + + 0 + 0 + + + + Owner: + + + + + + + + 0 + 0 + + + + Read + + + + + + + + 0 + 0 + + + + Write + + + + + + + + 0 + 0 + + + + Execute + + + + + + + + 0 + 0 + + + + Group: + + + + + + + + 0 + 0 + + + + Read + + + + + + + + 0 + 0 + + + + Write + + + + + + + + 0 + 0 + + + + Execute + + + + + + + + 0 + 0 + + + + Other: + + + + + + + + 0 + 0 + + + + Read + + + + + + + + 0 + 0 + + + + Write + + + + + + + + 0 + 0 + + + + Execute + + + + + + + + + Sticky + + + + + + + SetUID + + + + + + + SetGID + + + + + + + Qt::Vertical + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + Advanced Mode + + + true + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + Fm::AppChooserComboBox + QComboBox +
appchoosercombobox.h
+
+
+ + + + buttonBox + accepted() + FilePropsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + FilePropsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/src/filelauncher.cpp b/src/filelauncher.cpp new file mode 100644 index 0000000..1bee905 --- /dev/null +++ b/src/filelauncher.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "filelauncher.h" +#include "applaunchcontext.h" +#include +#include +#include "execfiledialog_p.h" +#include "appchooserdialog.h" +#include "utilities.h" + +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; +} + + +} // namespace Fm diff --git a/src/filelauncher.h b/src/filelauncher.h new file mode 100644 index 0000000..81c1dc6 --- /dev/null +++ b/src/filelauncher.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_FILELAUNCHER_H +#define FM_FILELAUNCHER_H + +#include "libfmqtglobals.h" +#include +#include + +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(user_data)->getApp(file_infos, mime_type, err); + } + static gboolean _openFolder(GAppLaunchContext* ctx, GList* folder_infos, gpointer user_data, GError** err) { + return reinterpret_cast(user_data)->openFolder(ctx, folder_infos, err); + } + static FmFileLauncherExecAction _execFile(FmFileInfo* file, gpointer user_data) { + return reinterpret_cast(user_data)->execFile(file); + } + static gboolean _error(GAppLaunchContext* ctx, GError* err, FmPath* file, gpointer user_data) { + return reinterpret_cast(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(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 diff --git a/src/filemenu.cpp b/src/filemenu.cpp new file mode 100644 index 0000000..f5692bd --- /dev/null +++ b/src/filemenu.cpp @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#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 +#endif +#include +#include +#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(); + + 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_); + + separator2_ = 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); + } + } + } + } + + separator3_ = 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(sender()); + openFilesWithApp(action->appInfo()); +} + +#ifdef CUSTOM_ACTIONS +void FileMenu::onCustomActionTrigerred() { + CustomAction* action = static_cast(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 diff --git a/src/filemenu.h b/src/filemenu.h new file mode 100644 index 0000000..45e9e69 --- /dev/null +++ b/src/filemenu.h @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_FILEMENU_H +#define FM_FILEMENU_H + +#include "libfmqtglobals.h" +#include +#include +#include + +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* createAction() { + return createAction_; + } + + QAction* separator2() { + return separator2_; + } + + 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* separator3() { + return separator3_; + } + + 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* createAction_; + QAction* separator2_; + QAction* cutAction_; + QAction* copyAction_; + QAction* pasteAction_; + QAction* deleteAction_; + QAction* unTrashAction_; + QAction* renameAction_; + QAction* separator3_; + QAction* propertiesAction_; + + FileLauncher* fileLauncher_; +}; + +} + +#endif // FM_FILEMENU_H diff --git a/src/filemenu_p.h b/src/filemenu_p.h new file mode 100644 index 0000000..94376d8 --- /dev/null +++ b/src/filemenu_p.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef FM_FILEMENU_P_H +#define FM_FILEMENU_P_H + +#include "icontheme.h" +#ifdef CUSTOM_ACTIONS +#include +#endif +#include + +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(fm_file_action_item_ref(item))) { + const char* icon_name = fm_file_action_item_get_icon(item); + if(icon_name) + setIcon(QIcon::fromTheme(icon_name)); + } + + virtual ~CustomAction() { + fm_file_action_item_unref(item_); + } + + FmFileActionItem* item() { + return item_; + } + +private: + FmFileActionItem* item_; +}; + +#endif + +} // namespace Fm + +#endif diff --git a/src/fileoperation.cpp b/src/fileoperation.cpp new file mode 100644 index 0000000..8026b8f --- /dev/null +++ b/src/fileoperation.cpp @@ -0,0 +1,307 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "fileoperation.h" +#include "fileoperationdialog.h" +#include +#include +#include +#include + +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(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; +} + + +} // namespace Fm diff --git a/src/fileoperation.h b/src/fileoperation.h new file mode 100644 index 0000000..7f8e67a --- /dev/null +++ b/src/fileoperation.h @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_FILEOPERATION_H +#define FM_FILEOPERATION_H + +#include "libfmqtglobals.h" +#include +#include +#include + +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 diff --git a/src/fileoperationdialog.cpp b/src/fileoperationdialog.cpp new file mode 100644 index 0000000..cbb409b --- /dev/null +++ b/src/fileoperationdialog.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "fileoperationdialog.h" +#include "fileoperation.h" +#include "renamedialog.h" +#include +#include "ui_file-operation-dialog.h" + +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(); +} + + +} // namespace Fm diff --git a/src/fileoperationdialog.h b/src/fileoperationdialog.h new file mode 100644 index 0000000..664f701 --- /dev/null +++ b/src/fileoperationdialog.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_FILEOPERATIONDIALOG_H +#define FM_FILEOPERATIONDIALOG_H + +#include "libfmqtglobals.h" +#include +#include + +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 diff --git a/src/filepropsdialog.cpp b/src/filepropsdialog.cpp new file mode 100644 index 0000000..824dc3f --- /dev/null +++ b/src/filepropsdialog.cpp @@ -0,0 +1,444 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "filepropsdialog.h" +#include "ui_file-props.h" +#include "icontheme.h" +#include "utilities.h" +#include "fileoperation.h" +#include +#include +#include +#include +#include +#include + +#define DIFFERENT_UIDS ((uid)-1) +#define DIFFERENT_GIDS ((gid)-1) +#define DIFFERENT_PERMS ((mode_t)-1) + +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); + gboolean ret = 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); + } + } +} + + +} // namespace Fm diff --git a/src/filepropsdialog.h b/src/filepropsdialog.h new file mode 100644 index 0000000..257f926 --- /dev/null +++ b/src/filepropsdialog.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_FILEPROPSDIALOG_H +#define FM_FILEPROPSDIALOG_H + +#include "libfmqtglobals.h" +#include +#include +#include + +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 diff --git a/src/filesearch.ui b/src/filesearch.ui new file mode 100644 index 0000000..f5f6051 --- /dev/null +++ b/src/filesearch.ui @@ -0,0 +1,449 @@ + + + SearchDialog + + + + 0 + 0 + 512 + 420 + + + + Search Files + + + + + + + + + + + 0 + + + + Name/Location + + + + + + File Name Patterns: + + + + + + * + + + + + + + Case insensitive + + + + + + + Use regular expression + + + + + + + + + + Places to Search: + + + + + + + + + + + + + &Add + + + + + + + + + + + + &Remove + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Search in sub directories + + + + + + + Search for hidden files + + + + + + + + + + + File Type + + + + + + Only search for files of following types: + + + + + + Text files + + + + + + + Image files + + + + + + + Audio files + + + + + + + Video files + + + + + + + Documents + + + + + + + Folders + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + Content + + + + + + File contains: + + + + + + + + + Case insensiti&ve + + + + + + + &Use regular expression + + + + + + + + + + Qt::Vertical + + + + 20 + 186 + + + + + + + + + Properties + + + + + + File Size: + + + + + + Larger than: + + + + + + + + + + + + 2 + + + + Bytes + + + + + KiB + + + + + MiB + + + + + GiB + + + + + + + + + + Smaller than: + + + + + + + + + + + + 2 + + + + Bytes + + + + + KiB + + + + + MiB + + + + + GiB + + + + + + + + + + + + + Last Modified Time: + + + + + + Earlier than: + + + + + + + Later than: + + + + + + + true + + + + + + + true + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + SearchDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + SearchDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/filesearchdialog.cpp b/src/filesearchdialog.cpp new file mode 100644 index 0000000..eeaeaa3 --- /dev/null +++ b/src/filesearchdialog.cpp @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "filesearchdialog.h" +#include +#include "fm-search.h" +#include "ui_filesearch.h" +#include +#include + +namespace Fm { + +FileSearchDialog::FileSearchDialog(QStringList paths, QWidget* parent, Qt::WindowFlags f): + QDialog(parent, f), + ui(new Ui::SearchDialog()) { + ui->setupUi(this); + ui->minSize->setMaximum(std::numeric_limits().max()); + ui->maxSize->setMaximum(std::numeric_limits().max()); + Q_FOREACH(const QString& path, paths) { + ui->listView->addItem(path); + } + + ui->maxTime->setDate(QDate::currentDate()); + ui->minTime->setDate(QDate::currentDate()); + + connect(ui->addPath, &QPushButton::clicked, this, &FileSearchDialog::onAddPath); + connect(ui->removePath, &QPushButton::clicked, this, &FileSearchDialog::onRemovePath); +} + +FileSearchDialog::~FileSearchDialog() { + delete ui; +} + +void FileSearchDialog::accept() { + // build the search:/// uri + int n = ui->listView->count(); + if(n > 0) { + FmSearch* search = fm_search_new(); + int i; + for(i = 0; i < n; ++i) { // add directories + QListWidgetItem* item = ui->listView->item(i); + fm_search_add_dir(search, item->text().toLocal8Bit().constData()); + } + + fm_search_set_recursive(search, ui->recursiveSearch->isChecked()); + fm_search_set_show_hidden(search, ui->searchHidden->isChecked()); + fm_search_set_name_patterns(search, ui->namePatterns->text().toUtf8().constData()); + fm_search_set_name_ci(search, ui->nameCaseInsensitive->isChecked()); + fm_search_set_name_regex(search, ui->nameRegExp->isChecked()); + + fm_search_set_content_pattern(search, ui->contentPattern->text().toUtf8().constData()); + fm_search_set_content_ci(search, ui->contentCaseInsensitive->isChecked()); + fm_search_set_content_regex(search, ui->contentRegExp->isChecked()); + + // search for the files of specific mime-types + if(ui->searchTextFiles->isChecked()) + fm_search_add_mime_type(search, "text/plain"); + if(ui->searchImages->isChecked()) + fm_search_add_mime_type(search, "image/*"); + if(ui->searchAudio->isChecked()) + fm_search_add_mime_type(search, "audio/*"); + if(ui->searchVideo->isChecked()) + fm_search_add_mime_type(search, "video/*"); + if(ui->searchFolders->isChecked()) + fm_search_add_mime_type(search, "inode/directory"); + if(ui->searchDocuments->isChecked()) { + const char* doc_types[] = { + "application/pdf", + /* "text/html;" */ + "application/vnd.oasis.opendocument.*", + "application/vnd.openxmlformats-officedocument.*", + "application/msword;application/vnd.ms-word", + "application/msexcel;application/vnd.ms-excel" + }; + for(i = 0; i < sizeof(doc_types)/sizeof(char*); ++i) + fm_search_add_mime_type(search, doc_types[i]); + } + + // search based on file size + const unsigned int unit_bytes[] = {1, (1024), (1024*1024), (1024*1024*1024)}; + if(ui->largerThan->isChecked()) { + guint64 size = ui->minSize->value() * unit_bytes[ui->minSizeUnit->currentIndex()]; + fm_search_set_min_size(search, size); + } + + if(ui->smallerThan->isChecked()) { + guint64 size = ui->maxSize->value() * unit_bytes[ui->maxSizeUnit->currentIndex()]; + fm_search_set_min_size(search, size); + } + + // search based on file mtime (we only support date in YYYY-MM-DD format) + if(ui->earlierThan->isChecked()) { + fm_search_set_max_mtime(search, ui->maxTime->date().toString(QStringLiteral("yyyy-MM-dd")).toUtf8().constData()); + } + if(ui->laterThan->isChecked()) { + fm_search_set_min_mtime(search, ui->minTime->date().toString(QStringLiteral("yyyy-MM-dd")).toUtf8().constData()); + } + + searchUri_.take(fm_search_dup_path(search)); + + fm_search_free(search); + } + else { + QMessageBox::critical(this, tr("Error"), tr("You should add at least add one directory to search.")); + return; + } + QDialog::accept(); +} + +void FileSearchDialog::onAddPath() { + QString dir = QFileDialog::getExistingDirectory(this, tr("Select a folder")); + if(dir.isEmpty()) + return; + // avoid adding duplicated items + if(ui->listView->findItems(dir, Qt::MatchFixedString|Qt::MatchCaseSensitive).isEmpty()) { + ui->listView->addItem(dir); + } +} + +void FileSearchDialog::onRemovePath() { + // remove selected items + Q_FOREACH(QListWidgetItem* item, ui->listView->selectedItems()) { + delete item; + } +} + +} diff --git a/src/filesearchdialog.h b/src/filesearchdialog.h new file mode 100644 index 0000000..dc6dc08 --- /dev/null +++ b/src/filesearchdialog.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef FM_FILESEARCHDIALOG_H +#define FM_FILESEARCHDIALOG_H + +#include "libfmqtglobals.h" +#include +#include "path.h" + +namespace Ui { + class SearchDialog; +} + +namespace Fm { + +class LIBFM_QT_API FileSearchDialog : public QDialog +{ +public: + FileSearchDialog(QStringList paths = QStringList(), QWidget * parent = 0, Qt::WindowFlags f = 0); + ~FileSearchDialog(); + + Path searchUri() const { + return searchUri_; + } + + virtual void accept(); + +private Q_SLOTS: + void onAddPath(); + void onRemovePath(); + +private: + Ui::SearchDialog* ui; + Path searchUri_; +}; + +} + +#endif // FM_FILESEARCHDIALOG_H diff --git a/src/fm-search.c b/src/fm-search.c new file mode 100644 index 0000000..4298848 --- /dev/null +++ b/src/fm-search.c @@ -0,0 +1,317 @@ +/* + * fm-search-uri.c + * + * Copyright 2015 Hong Jen Yee (PCMan) + * Copyright 2012-2014 Andriy Grytsenko (LStranger) + * + * 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 "fm-search.h" +#include + +struct _FmSearch +{ + gboolean recursive; + gboolean show_hidden; + char* name_patterns; + gboolean name_ci; + gboolean name_regex; + char* content_pattern; + gboolean content_ci; + gboolean content_regex; + GList* mime_types; + GList* search_path_list; + guint64 max_size; + guint64 min_size; + char* max_mtime; + char* min_mtime; +}; + +FmSearch* fm_search_new (void) +{ + FmSearch* search = (FmSearch*)g_slice_new0(FmSearch); + return search; +} + +void fm_search_free(FmSearch* search) +{ + g_list_free_full(search->mime_types, (GDestroyNotify)g_free); + g_list_free_full(search->search_path_list, (GDestroyNotify)g_free); + g_free(search->name_patterns); + g_free(search->content_pattern); + g_free(search->max_mtime); + g_free(search->min_mtime); + g_slice_free(FmSearch, search); +} + +gboolean fm_search_get_recursive(FmSearch* search) +{ + return search->recursive; +} + +void fm_search_set_recursive(FmSearch* search, gboolean recursive) +{ + search->recursive = recursive; +} + +gboolean fm_search_get_show_hidden(FmSearch* search) +{ + return search->show_hidden; +} + +void fm_search_set_show_hidden(FmSearch* search, gboolean show_hidden) +{ + search->show_hidden = show_hidden; +} + +const char* fm_search_get_name_patterns(FmSearch* search) +{ + return search->name_patterns; +} + +void fm_search_set_name_patterns(FmSearch* search, const char* name_patterns) +{ + g_free(search->name_patterns); + search->name_patterns = g_strdup(name_patterns); +} + +gboolean fm_search_get_name_ci(FmSearch* search) +{ + return search->name_ci; +} + +void fm_search_set_name_ci(FmSearch* search, gboolean name_ci) +{ + search->name_ci = name_ci; +} + +gboolean fm_search_get_name_regex(FmSearch* search) +{ + return search->name_regex; +} + +void fm_search_set_name_regex(FmSearch* search, gboolean name_regex) +{ + search->name_regex = name_regex; +} + +const char* fm_search_get_content_pattern(FmSearch* search) +{ + return search->content_pattern; +} + +void fm_search_set_content_pattern(FmSearch* search, const char* content_pattern) +{ + g_free(search->content_pattern); + search->content_pattern = g_strdup(content_pattern); +} + +gboolean fm_search_get_content_ci(FmSearch* search) +{ + return search->content_ci; +} + +void fm_search_set_content_ci(FmSearch* search, gboolean content_ci) +{ + search->content_ci = content_ci; +} + +gboolean fm_search_get_content_regex(FmSearch* search) +{ + return search->content_regex; +} + +void fm_search_set_content_regex(FmSearch* search, gboolean content_regex) +{ + search->content_regex = content_regex; +} + +void fm_search_add_dir(FmSearch* search, const char* dir) +{ + GList* l = g_list_find_custom(search->search_path_list, dir, (GCompareFunc)strcmp); + if(!l) + search->search_path_list = g_list_prepend(search->search_path_list, g_strdup(dir)); +} + +void fm_search_remove_dir(FmSearch* search, const char* dir) +{ + GList* l = g_list_find_custom(search->search_path_list, dir, (GCompareFunc)strcmp); + if(G_LIKELY(l)) + { + g_free(l->data); + search->search_path_list = g_list_delete_link(search->search_path_list, l); + } +} + +GList* fm_search_get_dirs(FmSearch* search) +{ + return search->search_path_list; +} + +void fm_search_add_mime_type(FmSearch* search, const char* mime_type) +{ + GList* l = g_list_find_custom(search->mime_types, mime_type, (GCompareFunc)strcmp); + if(!l) + search->mime_types = g_list_prepend(search->mime_types, g_strdup(mime_type)); +} + +void fm_search_remove_mime_type(FmSearch* search, const char* mime_type) +{ + GList* l = g_list_find_custom(search->mime_types, mime_type, (GCompareFunc)strcmp); + if(G_LIKELY(l)) + { + g_free(l->data); + search->mime_types = g_list_delete_link(search->mime_types, l); + } +} + +GList* fm_search_get_mime_types(FmSearch* search) +{ + return search->mime_types; +} + +guint64 fm_search_get_max_size(FmSearch* search) +{ + return search->max_size; +} + +void fm_search_set_max_size(FmSearch* search, guint64 size) +{ + search->max_size = size; +} + +guint64 fm_search_get_min_size(FmSearch* search) +{ + return search->min_size; +} + +void fm_search_set_min_size(FmSearch* search, guint64 size) +{ + search->min_size = size; +} + +/* format of mtime: YYYY-MM-DD */ +const char* fm_search_get_max_mtime(FmSearch* search) +{ + return search->max_mtime; +} + +void fm_search_set_max_mtime(FmSearch* search, const char* mtime) +{ + g_free(search->max_mtime); + search->max_mtime = g_strdup(mtime); +} + +/* format of mtime: YYYY-MM-DD */ +const char* fm_search_get_min_mtime(FmSearch* search) +{ + return search->min_mtime; +} + +void fm_search_set_min_mtime(FmSearch* search, const char* mtime) +{ + g_free(search->min_mtime); + search->min_mtime = g_strdup(mtime); +} + +/* really build the path */ +FmPath* fm_search_dup_path(FmSearch* search) +{ + FmPath* search_path = NULL; + GString* search_str = g_string_sized_new(1024); + /* build the search:// URI to perform the search */ + g_string_append(search_str, "search://"); + + if(search->search_path_list) /* we need to have at least one dir path */ + { + char *escaped; + /* add paths */ + GList* l; + for(l = search->search_path_list; ; ) + { + char *path_str = (char*)l->data; + /* escape possible '?' and ',' */ + escaped = g_uri_escape_string(path_str, "!$&'()*+:;=/@", TRUE); + g_string_append(search_str, escaped); + g_free(escaped); + + l = l->next; + if(!l) /* no more items */ + break; + g_string_append_c(search_str, ','); /* separator for paths */ + } + + g_string_append_c(search_str, '?'); + g_string_append_printf(search_str, "recursive=%c", search->recursive ? '1' : '0'); + g_string_append_printf(search_str, "&show_hidden=%c", search->show_hidden ? '1' : '0'); + if(search->name_patterns && *search->name_patterns) + { + /* escape ampersands in pattern */ + escaped = g_uri_escape_string(search->name_patterns, ":/?#[]@!$'()*+,;", TRUE); + if(search->name_regex) + g_string_append_printf(search_str, "&name_regex=%s", escaped); + else + g_string_append_printf(search_str, "&name=%s", escaped); + if(search->name_ci) + g_string_append_printf(search_str, "&name_ci=%c", search->name_ci ? '1' : '0'); + g_free(escaped); + } + + if(search->content_pattern && *search->content_pattern) + { + /* escape ampersands in pattern */ + escaped = g_uri_escape_string(search->content_pattern, ":/?#[]@!$'()*+,;^<>{}", TRUE); + if(search->content_regex) + g_string_append_printf(search_str, "&content_regex=%s", escaped); + else + g_string_append_printf(search_str, "&content=%s", escaped); + g_free(escaped); + if(search->content_ci) + g_string_append_printf(search_str, "&content_ci=%c", search->content_ci ? '1' : '0'); + } + + /* search for the files of specific mime-types */ + if(search->mime_types) + { + GList* l; + g_string_append(search_str, "&mime_types="); + for(l = search->mime_types; l; l=l->next) + { + const char* mime_type = (const char*)l->data; + g_string_append(search_str, mime_type); + if(l->next) + g_string_append_c(search_str, ';'); + } + } + + if(search->min_size) + g_string_append_printf(search_str, "&min_size=%llu", (unsigned long long)search->min_size); + + if(search->max_size) + g_string_append_printf(search_str, "&max_size=%llu", (unsigned long long)search->max_size); + + if(search->min_mtime) + g_string_append_printf(search_str, "&min_mtime=%s", search->min_mtime); + + if(search->max_mtime) + g_string_append_printf(search_str, "&max_mtime=%s", search->max_mtime); + + search_path = fm_path_new_for_uri(search_str->str); + g_string_free(search_str, TRUE); + } + return search_path; +} diff --git a/src/fm-search.h b/src/fm-search.h new file mode 100644 index 0000000..c99f71d --- /dev/null +++ b/src/fm-search.h @@ -0,0 +1,89 @@ +/* + * fm-search-uri.h + * + * Copyright 2015 Hong Jen Yee (PCMan) + * Copyright 2012-2014 Andriy Grytsenko (LStranger) + * + * 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 + * + */ + +/* FmSearch implements a tool used to generate a search:// URI used by libfm to search for files. + * This API might become part of libfm in the future. + */ + +#ifndef _FM_SEARCH_H_ +#define _FM_SEARCH_H_ + +#include + +G_BEGIN_DECLS + +typedef struct _FmSearch FmSearch; + +FmSearch* fm_search_new(void); +void fm_search_free(FmSearch* search); + +FmPath* fm_search_dup_path(FmSearch* search); + +gboolean fm_search_get_recursive(FmSearch* search); +void fm_search_set_recursive(FmSearch* search, gboolean recursive); + +gboolean fm_search_get_show_hidden(FmSearch* search); +void fm_search_set_show_hidden(FmSearch* search, gboolean show_hidden); + +const char* fm_search_get_name_patterns(FmSearch* search); +void fm_search_set_name_patterns(FmSearch* search, const char* name_patterns); + +gboolean fm_search_get_name_ci(FmSearch* search); +void fm_search_set_name_ci(FmSearch* search, gboolean name_ci); + +gboolean fm_search_get_name_regex(FmSearch* search); +void fm_search_set_name_regex(FmSearch* search, gboolean name_regex); + +const char* fm_search_get_content_pattern(FmSearch* search); +void fm_search_set_content_pattern(FmSearch* search, const char* content_pattern); + +gboolean fm_search_get_content_ci(FmSearch* search); +void fm_search_set_content_ci(FmSearch* search, gboolean content_ci); + +gboolean fm_search_get_content_regex(FmSearch* search); +void fm_search_set_content_regex(FmSearch* search, gboolean content_regex); + +void fm_search_add_dir(FmSearch* search, const char* dir); +void fm_search_remove_dir(FmSearch* search, const char* dir); +GList* fm_search_get_dirs(FmSearch* search); + +void fm_search_add_mime_type(FmSearch* search, const char* mime_type); +void fm_search_remove_mime_type(FmSearch* search, const char* mime_type); +GList* fm_search_get_mime_types(FmSearch* search); + +guint64 fm_search_get_max_size(FmSearch* search); +void fm_search_set_max_size(FmSearch* search, guint64 size); + +guint64 fm_search_get_min_size(FmSearch* search); +void fm_search_set_min_size(FmSearch* search, guint64 size); + +/* format of mtime: YYYY-MM-DD */ +const char* fm_search_get_max_mtime(FmSearch* search); +void fm_search_set_max_mtime(FmSearch* search, const char* mtime); + +/* format of mtime: YYYY-MM-DD */ +const char* fm_search_get_min_mtime(FmSearch* search); +void fm_search_set_min_mtime(FmSearch* search, const char* mtime); + +G_END_DECLS + +#endif /* _FM_SEARCH_H_ */ diff --git a/src/folderitemdelegate.cpp b/src/folderitemdelegate.cpp new file mode 100644 index 0000000..d1cfe66 --- /dev/null +++ b/src/folderitemdelegate.cpp @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "folderitemdelegate.h" +#include "foldermodel.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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(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; + + Q_ASSERT(gridSize_ != QSize()); + QRectF textRect(0, 0, gridSize_.width(), gridSize_.height() - opt.decorationSize.height()); + drawText(nullptr, opt, textRect); // passing NULL for painter will calculate the bounding rect only. + int width = qMax((int)textRect.width(), opt.decorationSize.width()); + int height = opt.decorationSize.height() + textRect.height(); + return QSize(width, height); + } + return QStyledItemDelegate::sizeHint(option, index); +} + +QIcon::Mode FolderItemDelegate::iconModeFromState(const QStyle::State state) { + + if(state & QStyle::State_Enabled) + return (state & QStyle::State_Selected) ? QIcon::Selected : QIcon::Normal; + + return QIcon::Disabled; +} + +// 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(index.data(FolderModel::FileInfoRole).value()); + 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 + // The text rect dimensions should be exactly as they were in sizeHint() + QRectF textRect(opt.rect.x() - (gridSize_.width() - opt.rect.width()) / 2, + opt.rect.y() + opt.decorationSize.height(), + gridSize_.width(), + gridSize_.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 nullptr, 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; + textRect.adjust(2, 2, -2, -2); // a 2-px margin is considered at FolderView::updateGridSize() + 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(); + width = qMax(width, (qreal)opt.fontMetrics.width(elidedText)); + + // draw background for selected item + QRectF boundRect = layout.boundingRect(); + //qDebug() << "bound rect: " << boundRect << "width: " << width; + boundRect.setWidth(width); + boundRect.setHeight(height); + boundRect.moveTo(textRect.x() + (textRect.width() - width)/2, textRect.y()); + + QRectF selRect = boundRect.adjusted(-2, -2, 2, 2); + + if(!painter) { // no painter, calculate the bounding rect only + textRect = selRect; + return; + } + + QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; + if(opt.state & QStyle::State_Selected) { + painter->fillRect(selRect, 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(boundRect.x() + line.position().x(), boundRect.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 = selRect.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 diff --git a/src/folderitemdelegate.h b/src/folderitemdelegate.h new file mode 100644 index 0000000..8df5b42 --- /dev/null +++ b/src/folderitemdelegate.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_FOLDERITEMDELEGATE_H +#define FM_FOLDERITEMDELEGATE_H + +#include "libfmqtglobals.h" +#include +#include + +namespace Fm { + +class LIBFM_QT_API FolderItemDelegate : public QStyledItemDelegate { + Q_OBJECT +public: + explicit FolderItemDelegate(QAbstractItemView* view, QObject* parent = nullptr); + virtual ~FolderItemDelegate(); + + inline void setGridSize(QSize size) { + gridSize_ = size; + } + + inline QSize gridSize() const { + 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 diff --git a/src/foldermenu.cpp b/src/foldermenu.cpp new file mode 100644 index 0000000..bd1bb4a --- /dev/null +++ b/src/foldermenu.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * Copyright (C) 2012 - 2014 Andriy Grytsenko (LStranger) + * + * 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 "foldermenu.h" +#include "createnewmenu.h" +#include "filepropsdialog.h" +#include "folderview.h" +#include "utilities.h" +#include // 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(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(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 diff --git a/src/foldermenu.h b/src/foldermenu.h new file mode 100644 index 0000000..fd2cbb4 --- /dev/null +++ b/src/foldermenu.h @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_FOLDERMENU_H +#define FM_FOLDERMENU_H + +#include "libfmqtglobals.h" +#include +#include +#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 diff --git a/src/foldermodel.cpp b/src/foldermodel.cpp new file mode 100644 index 0000000..1d90076 --- /dev/null +++ b/src/foldermodel.cpp @@ -0,0 +1,554 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "foldermodel.h" +#include "icontheme.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "utilities.h" +#include "fileoperation.h" +#include "thumbnailloader.h" + +namespace Fm { + +FolderModel::FolderModel() : + folder_(nullptr) { +/* + 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(nullptr); + + // 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_ = nullptr; +} + +void FolderModel::onStartLoading(FmFolder* folder, gpointer user_data) { + FolderModel* model = static_cast(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(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(user_data); + for(GSList* l = files; l; l = l->next) { + FmFileInfo* info = FM_FILE_INFO(l->data); + int row; + QList::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(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::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(index.internalPointer()); +} + +FmFileInfo* FolderModel::fileInfoFromIndex(const QModelIndex& index) const { + FolderModelItem* item = itemFromIndex(index); + return item ? item->info : nullptr; +} + +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::iterator FolderModel::findItemByPath(FmPath* path, int* row) { + QList::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::iterator FolderModel::findItemByName(const char* name, int* row) { + QList::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::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); + + for(const auto &index : indexes) { + 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(const int size) { + QVector>::iterator it = thumbnailRefCounts.begin(); + while (it != thumbnailRefCounts.end()) { + if (it->first == size) { + ++it->second; + return; + } else ++it; + } + thumbnailRefCounts.append(QPair(size, 1)); +} + +// ask the model to free cached thumbnails of the specified size +void FolderModel::releaseThumbnails(int size) { + QVector >::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::iterator it; + for(it = thumbnailResults.begin(); it != thumbnailResults.end();) { + QLinkedList::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::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(user_data); + QLinkedList::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::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:; + } + } + return QImage(); +} + +void FolderModel::updateIcons() { + QList::iterator it = items.begin(); + for(;it != items.end(); ++it) { + (*it).updateIcon(); + } +} + + +} // namespace Fm diff --git a/src/foldermodel.h b/src/foldermodel.h new file mode 100644 index 0000000..4ae0edb --- /dev/null +++ b/src/foldermodel.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_FOLDERMODEL_H +#define FM_FOLDERMODEL_H + +#include "libfmqtglobals.h" +#include +#include +#include +#include +#include +#include +#include +#include +#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::iterator findItemByPath(FmPath* path, int* row); + QList::iterator findItemByName(const char* name, int* row); + QList::iterator findItemByFileInfo(FmFileInfo* info, int* row); + +private: + FmFolder* folder_; + // FIXME: should we use a hash table here so item lookup becomes much faster? + QList items; + + // record what size of thumbnails we should cache in an array of pairs. + QVector > thumbnailRefCounts; + QLinkedList thumbnailResults; +}; + +} + +#endif // FM_FOLDERMODEL_H diff --git a/src/foldermodelitem.cpp b/src/foldermodelitem.cpp new file mode 100644 index 0000000..ef1a24b --- /dev/null +++ b/src/foldermodelitem.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "foldermodelitem.h" + +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::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::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::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 + + +} // namespace Fm diff --git a/src/foldermodelitem.h b/src/foldermodelitem.h new file mode 100644 index 0000000..7274057 --- /dev/null +++ b/src/foldermodelitem.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_FOLDERMODELITEM_H +#define FM_FOLDERMODELITEM_H + +#include "libfmqtglobals.h" +#include +#include +#include +#include +#include +#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 thumbnails; +}; + +} + +#endif // FM_FOLDERMODELITEM_H diff --git a/src/folderview.cpp b/src/folderview.cpp new file mode 100644 index 0000000..d47974c --- /dev/null +++ b/src/folderview.cpp @@ -0,0 +1,984 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "folderview.h" +#include "foldermodel.h" +#include +#include +#include +#include "proxyfoldermodel.h" +#include "folderitemdelegate.h" +#include "dndactionmenu.h" +#include "filemenu.h" +#include "foldermenu.h" +#include "filelauncher.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "folderview_p.h" + +Q_DECLARE_OPAQUE_POINTER(FmFileInfo*) + +using 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(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(itemDelegateForColumn(FolderModel::ColumnFileName)); + // Q_ASSERT(delegate != nullptr); + // 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(parent())->childDragEnterEvent(event); +} + +void FolderViewListView::dragLeaveEvent(QDragLeaveEvent* e) { + if(movement() != Static) + QListView::dragLeaveEvent(e); + else + QAbstractItemView::dragLeaveEvent(e); + static_cast(parent())->childDragLeaveEvent(e); +} + +void FolderViewListView::dragMoveEvent(QDragMoveEvent* e) { + if(movement() != Static) + QListView::dragMoveEvent(e); + else + QAbstractItemView::dragMoveEvent(e); + static_cast(parent())->childDragMoveEvent(e); +} + +void FolderViewListView::dropEvent(QDropEvent* e) { + + static_cast(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, nullptr, 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, nullptr, 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_(nullptr), + 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(model)) { + connect(proxyModel, &ProxyFolderModel::sortFilterChanged, this, &FolderViewTreeView::onSortFilterChanged, + Qt::UniqueConnection); + onSortFilterChanged(); + } +} + +void FolderViewTreeView::mousePressEvent(QMouseEvent* event) { + QTreeView::mousePressEvent(event); + static_cast(parent())->childMousePressEvent(event); +} + +void FolderViewTreeView::dragEnterEvent(QDragEnterEvent* event) { + QTreeView::dragEnterEvent(event); + static_cast(parent())->childDragEnterEvent(event); +} + +void FolderViewTreeView::dragLeaveEvent(QDragLeaveEvent* e) { + QTreeView::dragLeaveEvent(e); + static_cast(parent())->childDragLeaveEvent(e); +} + +void FolderViewTreeView::dragMoveEvent(QDragMoveEvent* e) { + QTreeView::dragMoveEvent(e); + static_cast(parent())->childDragMoveEvent(e); +} + +void FolderViewTreeView::dropEvent(QDropEvent* e) { + static_cast(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_ = nullptr; + } +} + +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, nullptr, 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, nullptr, 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(model())) { + header()->setSortIndicatorShown(true); + header()->setSortIndicator(proxyModel->sortColumn(), proxyModel->sortOrder()); + if (!isSortingEnabled()) { + setSortingEnabled(true); + } + } +} + + +//----------------------------------------------------------------------------- + +FolderView::FolderView(ViewMode _mode, QWidget* parent): + QWidget(parent), + view(nullptr), + mode((ViewMode)0), + autoSelectionDelay_(600), + autoSelectionTimer_(nullptr), + selChangedTimer_(nullptr), + fileLauncher_(nullptr), + model_(nullptr) { + + 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(); + if(info) { + if (!(QApplication::keyboardModifiers() & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier))) { + Q_EMIT clicked(ActivatedClick, info); + } + } + } +} + +void FolderView::onSelChangedTimeout() { + selChangedTimer_->deleteLater(); + selChangedTimer_ = nullptr; + + 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 = nullptr; + 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(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(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() * 13; + int textHeight = fm.lineSpacing() * 3; + grid.setWidth(qMax(icon.width(), textWidth) + 4); // a margin of 2 px for selection rects + grid.setHeight(icon.height() + textHeight + 4); // a margin of 2 px for selection rects + break; + } + default: + ; // do not use grid size + } + if(mode == IconMode || mode == ThumbnailMode) + listView->setGridSize(grid + QSize(6, 6)); // a margin of 6 px for every cell + else + listView->setGridSize(grid); + FolderItemDelegate* delegate = static_cast(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(data.value()); + 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, nullptr); + } + } +} + +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() : nullptr; +} + +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 nullptr; +} + +QModelIndex FolderView::indexFromFolderPath(FmPath* folderPath) const { + if(!model_ || !folderPath) return QModelIndex(); + QModelIndex index; + int count = model_->rowCount(); + for(int row = 0; row < count; ++row) { + index = model_->index(row, 0); + FmFileInfo* info = model_->fileInfoFromIndex(index); + if(info && fm_file_info_is_dir(info) && fm_path_equal(folderPath,fm_file_info_get_path(info))) + return index; + } + return QModelIndex(); +} + +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 nullptr; +} + +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(e->possibleActions(), 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(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_ = nullptr; +} + +void FolderView::onFileClicked(int type, FmFileInfo* fileInfo) { + if(type == ActivatedClick) { + if(fileLauncher_) { + GList* files = g_list_append(nullptr, fileInfo); + fileLauncher_->launchFiles(nullptr, files); + g_list_free(files); + } + } + else if(type == ContextMenuClick) { + FmPath* folderPath = nullptr; + 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 = nullptr; + 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) { +} + diff --git a/src/folderview.h b/src/folderview.h new file mode 100644 index 0000000..1ae2e49 --- /dev/null +++ b/src/folderview.h @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_FOLDERVIEW_H +#define FM_FOLDERVIEW_H + +#include "libfmqtglobals.h" +#include +#include +#include +#include +#include +#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 + }; + + 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(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; + QModelIndex indexFromFolderPath(FmPath* folderPath) 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 diff --git a/src/folderview_p.h b/src/folderview_p.h new file mode 100644 index 0000000..91c6b81 --- /dev/null +++ b/src/folderview_p.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_FOLDERVIEW_P_H +#define FM_FOLDERVIEW_P_H + +#include +#include +#include +#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); + } + + inline QStyleOptionViewItem getViewOptions() { + return viewOptions(); + } + +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 diff --git a/src/fontbutton.cpp b/src/fontbutton.cpp new file mode 100644 index 0000000..f4599de --- /dev/null +++ b/src/fontbutton.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "fontbutton.h" +#include +#include + +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(); +} + + +} // namespace Fm diff --git a/src/fontbutton.h b/src/fontbutton.h new file mode 100644 index 0000000..1caf19b --- /dev/null +++ b/src/fontbutton.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_FONTBUTTON_H +#define FM_FONTBUTTON_H + +#include "libfmqtglobals.h" +#include + + +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 diff --git a/src/icontheme.cpp b/src/icontheme.cpp new file mode 100644 index 0000000..1c73e66 --- /dev/null +++ b/src/icontheme.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "icontheme.h" +#include +#include +#include +#include +#include +#include + +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(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(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(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); +} + + +} // namespace Fm diff --git a/src/icontheme.h b/src/icontheme.h new file mode 100644 index 0000000..c8769b7 --- /dev/null +++ b/src/icontheme.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_ICONTHEME_H +#define FM_ICONTHEME_H + +#include "libfmqtglobals.h" +#include +#include +#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 diff --git a/src/libfm-qt.pc.in b/src/libfm-qt.pc.in new file mode 100644 index 0000000..11e35a3 --- /dev/null +++ b/src/libfm-qt.pc.in @@ -0,0 +1,12 @@ +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@LIBFM_QT_LIBRARY_NAME@ +Cflags: -I${includedir} diff --git a/src/libfmqt.cpp b/src/libfmqt.cpp new file mode 100644 index 0000000..c874150 --- /dev/null +++ b/src/libfmqt.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include +#include "libfmqt.h" +#include +#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 diff --git a/src/libfmqt.h b/src/libfmqt.h new file mode 100644 index 0000000..83ffd8e --- /dev/null +++ b/src/libfmqt.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_APPLICATION_H +#define FM_APPLICATION_H + +#include "libfmqtglobals.h" +#include +#include +#include + +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 diff --git a/src/libfmqtglobals.h b/src/libfmqtglobals.h new file mode 100644 index 0000000..1a6d4fe --- /dev/null +++ b/src/libfmqtglobals.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef _LIBFM_QT_GLOBALS_ +#define _LIBFM_QT_GLOBALS_ + +#include "fm-qt_export.h" + +#endif diff --git a/src/mount-operation-password.ui b/src/mount-operation-password.ui new file mode 100644 index 0000000..75ebf31 --- /dev/null +++ b/src/mount-operation-password.ui @@ -0,0 +1,205 @@ + + + MountOperationPasswordDialog + + + + 0 + 0 + 244 + 302 + + + + + 0 + 0 + + + + Mount + + + + + + false + + + false + + + + + + + 0 + 0 + + + + + + + + + + + Connect &anonymously + + + usernameGroup + + + + + + + Connect as u&ser: + + + usernameGroup + + + + + + + + + + + + + 0 + 0 + + + + &Username: + + + username + + + + + + + QLineEdit::Password + + + + + + + + 0 + 0 + + + + &Password: + + + password + + + + + + + &Domain: + + + domain + + + + + + + + + + + + Forget password &immediately + + + passwordGroup + + + + + + + Remember password until you &logout + + + passwordGroup + + + + + + + Remember &forever + + + passwordGroup + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + MountOperationPasswordDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + MountOperationPasswordDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + + + + + diff --git a/src/mountoperation.cpp b/src/mountoperation.cpp new file mode 100644 index 0000000..bc3e0b8 --- /dev/null +++ b/src/mountoperation.cpp @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "mountoperation.h" +#include // for _() +#include +#include +#include +#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 diff --git a/src/mountoperation.h b/src/mountoperation.h new file mode 100644 index 0000000..00b5849 --- /dev/null +++ b/src/mountoperation.h @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_MOUNTOPERATION_H +#define FM_MOUNTOPERATION_H + +#include "libfmqtglobals.h" +#include +#include +#include +#include +#include + +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(this)); + g_object_unref(gf); + } + + void mount(GVolume* volume) { + g_volume_mount(volume, G_MOUNT_MOUNT_NONE, op, cancellable_, (GAsyncReadyCallback)onMountVolumeFinished, new QPointer(this)); + } + + void unmount(GMount* mount) { + prepareUnmount(mount); + g_mount_unmount_with_operation(mount, G_MOUNT_UNMOUNT_NONE, op, cancellable_, (GAsyncReadyCallback)onUnmountMountFinished, new QPointer(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(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(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* pThis); + static void onMountVolumeFinished(GVolume* volume, GAsyncResult *res, QPointer* pThis); + static void onUnmountMountFinished(GMount* mount, GAsyncResult *res, QPointer* pThis); + static void onEjectMountFinished(GMount* mount, GAsyncResult *res, QPointer* pThis); + static void onEjectVolumeFinished(GVolume* volume, GAsyncResult *res, QPointer* pThis); + + void handleFinish(GError* error); + +private: + GMountOperation* op; + GCancellable* cancellable_; + QWidget* parent_; + bool running; + bool interactive_; + QEventLoop* eventLoop; + bool autoDestroy_; +}; + +} + +#endif // FM_MOUNTOPERATION_H diff --git a/src/mountoperationpassworddialog.cpp b/src/mountoperationpassworddialog.cpp new file mode 100644 index 0000000..4305b0a --- /dev/null +++ b/src/mountoperationpassworddialog.cpp @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "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 diff --git a/src/mountoperationpassworddialog_p.h b/src/mountoperationpassworddialog_p.h new file mode 100644 index 0000000..10ff58b --- /dev/null +++ b/src/mountoperationpassworddialog_p.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_MOUNTOPERATIONPASSWORDDIALOG_H +#define FM_MOUNTOPERATIONPASSWORDDIALOG_H + +#include "libfmqtglobals.h" +#include +#include + +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 diff --git a/src/mountoperationquestiondialog.cpp b/src/mountoperationquestiondialog.cpp new file mode 100644 index 0000000..a409e28 --- /dev/null +++ b/src/mountoperationquestiondialog.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "mountoperationquestiondialog_p.h" +#include "mountoperation.h" +#include + +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 diff --git a/src/mountoperationquestiondialog_p.h b/src/mountoperationquestiondialog_p.h new file mode 100644 index 0000000..b0a4bdb --- /dev/null +++ b/src/mountoperationquestiondialog_p.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_MOUNTOPERATIONQUESTIONDIALOG_H +#define FM_MOUNTOPERATIONQUESTIONDIALOG_H + +#include "libfmqtglobals.h" +#include +#include + +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 diff --git a/src/path.cpp b/src/path.cpp new file mode 100644 index 0000000..5af020f --- /dev/null +++ b/src/path.cpp @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "path.h" + +using namespace Fm; diff --git a/src/path.h b/src/path.h new file mode 100644 index 0000000..be4a610 --- /dev/null +++ b/src/path.h @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef FM_PATH_H +#define FM_PATH_H + +#include "libfmqtglobals.h" +#include +#include +#include + +namespace Fm { + +class LIBFM_QT_API Path { +public: + + Path(): data_(nullptr) { + } + + 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_) : nullptr) { + } + + 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(data_ != nullptr ? fm_path_get_parent(data_) : nullptr, 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 diff --git a/src/pathedit.cpp b/src/pathedit.cpp new file mode 100644 index 0000000..4e30ca6 --- /dev/null +++ b/src/pathedit.cpp @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "pathedit.h" +#include "pathedit_p.h" +#include +#include +#include +#include +#include +#include + +namespace Fm { + +void PathEditJob::runJob() { + GError *err = NULL; + GFileEnumerator* enu = g_file_enumerate_children(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? + 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 + Q_EMIT finished(); +} + + +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); + } +} + + +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_); + } + + // create a new job to do dir listing + PathEditJob* job = new PathEditJob(); + job->edit = this; + job->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. + job->dirName = fm_file_new_for_commandline_arg(currentPrefix_.toLocal8Bit().constData()); + // qDebug("load: %s", g_file_get_uri(data->dirName)); + cancellable_ = g_cancellable_new(); + job->cancellable = (GCancellable*)g_object_ref(cancellable_); + + // launch a new worker thread to handle the job + QThread* thread = new QThread(); + job->moveToThread(thread); + connect(thread, &QThread::started, job, &PathEditJob::runJob); + connect(thread, &QThread::finished, thread, &QObject::deleteLater); + connect(thread, &QThread::finished, job, &QObject::deleteLater); + connect(job, &PathEditJob::finished, this, &PathEdit::onJobFinished); + thread->start(QThread::LowPriority); +} + +void PathEdit::freeCompleter() { + if(cancellable_) { + g_cancellable_cancel(cancellable_); + g_object_unref(cancellable_); + cancellable_ = NULL; + } + model_->setStringList(QStringList()); +} + +// This slot is called from main thread so it's safe to access the GUI +void PathEdit::onJobFinished() { + PathEditJob* data = static_cast(sender()); + 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 diff --git a/src/pathedit.h b/src/pathedit.h new file mode 100644 index 0000000..2c10d5f --- /dev/null +++ b/src/pathedit.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_PATHEDIT_H +#define FM_PATHEDIT_H + +#include "libfmqtglobals.h" +#include +#include + +class QCompleter; +class QStringListModel; + +namespace Fm { + +class PathEditJob; + +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(); + void onJobFinished(); + +private: + QCompleter* completer_; + QStringListModel* model_; + QString currentPrefix_; + GCancellable* cancellable_; +}; + +} + +#endif // FM_PATHEDIT_H diff --git a/src/pathedit_p.h b/src/pathedit_p.h new file mode 100644 index 0000000..b91629c --- /dev/null +++ b/src/pathedit_p.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_PATHEDIT_P_H +#define FM_PATHEDIT_P_H + +#include +#include + +namespace Fm { + +class PathEdit; + +class PathEditJob : public QObject { + Q_OBJECT +public: + GCancellable* cancellable; + GFile* dirName; + QStringList subDirs; + PathEdit* edit; + bool triggeredByFocusInEvent; + + ~PathEditJob() { + g_object_unref(dirName); + g_object_unref(cancellable); + } + +Q_SIGNALS: + void finished(); + +public Q_SLOTS: + void runJob(); + +}; + +} + +#endif // FM_PATHEDIT_P_H diff --git a/src/placesmodel.cpp b/src/placesmodel.cpp new file mode 100644 index 0000000..bc34a92 --- /dev/null +++ b/src/placesmodel.cpp @@ -0,0 +1,608 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "placesmodel.h" +#include "icontheme.h" +#include +#include +#include +#include +#include +#include "utilities.h" +#include "placesmodelitem.h" + +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 */ + gboolean shadowed = FALSE; +#if GLIB_CHECK_VERSION(2, 20, 0) + shadowed = g_mount_is_shadowed(mount); +#endif + // according to gio API doc, a shadowed mount should not be visible to the user + if(shadowed) { + shadowedMounts_.push_back(mount); + continue; + } + else { + 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_); + } + + Q_FOREACH(GMount* mount, shadowedMounts_) { + g_object_unref(mount); + } +} + +// static +void PlacesModel::onTrashChanged(GFileMonitor* monitor, GFile* gf, GFile* other, GFileMonitorEvent evt, PlacesModel* pThis) { + QTimer::singleShot(0, pThis, SLOT(updateTrash())); +} + +void PlacesModel::updateTrash() { + + struct UpdateTrashData { + QPointer model; + GFile* gf; + UpdateTrashData(PlacesModel* _model) : model(_model) { + gf = fm_file_new_for_uri("trash:///"); + } + ~UpdateTrashData() { + g_object_unref(gf); + } + }; + + if(trashItem_) { + UpdateTrashData* data = new UpdateTrashData(this); + g_file_query_info_async(data->gf, G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT, G_FILE_QUERY_INFO_NONE, G_PRIORITY_LOW, NULL, + [](GObject *source_object, GAsyncResult *res, gpointer user_data) { + // the callback lambda function is called when the asyn query operation is finished + UpdateTrashData* data = reinterpret_cast(user_data); + PlacesModel* _this = data->model.data(); + if(_this != nullptr) { // ensure that our model object is not deleted yet + GFileInfo* inf = g_file_query_info_finish(data->gf, res, NULL); + if(inf) { + if(_this->trashItem_ != nullptr) { // it's possible that when we finish, the trash item is removed + guint32 n = g_file_info_get_attribute_uint32(inf, G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT); + const char* icon_name = n > 0 ? "user-trash-full" : "user-trash"; + FmIcon* icon = fm_icon_from_name(icon_name); + _this->trashItem_->setIcon(icon); + fm_icon_unref(icon); + } + g_object_unref(inf); + } + } + delete data; // free the data used for this async operation. + }, data); + } +} + +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(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(devicesRoot->child(i, 0)); + if(item->type() == PlacesModelItem::Volume) { + PlacesModelVolumeItem* volumeItem = static_cast(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(devicesRoot->child(i, 0)); + if(item->type() == PlacesModelItem::Mount) { + PlacesModelMountItem* mountItem = static_cast(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(bookmarksRoot->child(i, 0)); + if(item->bookmark() == bkitem) + return item; + } + return NULL; +} + +void PlacesModel::onMountAdded(GVolumeMonitor* monitor, GMount* mount, PlacesModel* pThis) { + // according to gio API doc, a shadowed mount should not be visible to the user +#if GLIB_CHECK_VERSION(2, 20, 0) + if(g_mount_is_shadowed(mount)) { + if(pThis->shadowedMounts_.indexOf(mount) == -1) + pThis->shadowedMounts_.push_back(G_MOUNT(g_object_ref(mount))); + return; + } +#endif + 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_, QString()); + pThis->devicesRoot->appendRow(QList() << item << eject_btn); + } + } +} + +void PlacesModel::onMountChanged(GVolumeMonitor* monitor, GMount* mount, PlacesModel* pThis) { + gboolean shadowed = FALSE; + // according to gio API doc, a shadowed mount should not be visible to the user +#if GLIB_CHECK_VERSION(2, 20, 0) + shadowed = g_mount_is_shadowed(mount); + // qDebug() << "changed:" << mount << shadowed; +#endif + PlacesModelMountItem* item = pThis->itemFromMount(mount); + if(item) { + if(shadowed) { // if a visible item becomes shadowed, remove it from the model + pThis->shadowedMounts_.push_back(G_MOUNT(g_object_ref(mount))); // remember the shadowed mount + pThis->devicesRoot->removeRow(item->row()); + } + else { // otherwise, update its status + item->update(); + } + } + else { +#if GLIB_CHECK_VERSION(2, 20, 0) + if(!shadowed) { // if a mount is unshadowed + int i = pThis->shadowedMounts_.indexOf(mount); + if(i != -1) { // a previously shadowed mount is unshadowed + pThis->shadowedMounts_.removeAt(i); + onMountAdded(monitor, mount, pThis); // add it to our model again + } + } +#endif + } +} + +void PlacesModel::onMountRemoved(GVolumeMonitor* monitor, GMount* mount, PlacesModel* pThis) { + GVolume* vol = g_mount_get_volume(mount); + // qDebug() << "mount removed" << mount << "volume umounted: " << vol; + if(vol) { + // a volume is being unmounted + // NOTE: Due to some problems of gvfs, sometimes the volume does not receive + // "change" signal so it does not update the eject button. Let's workaround + // this by calling onVolumeChanged() handler manually. (This is needed for mtp://) + onVolumeChanged(monitor, vol, pThis); + g_object_unref(vol); + } + else { // network mounts and others + PlacesModelMountItem* item = pThis->itemFromMount(mount); + if(item) { + pThis->devicesRoot->removeRow(item->row()); + } + } + +#if GLIB_CHECK_VERSION(2, 20, 0) + // NOTE: g_mount_is_shadowed() sometimes returns FALSE here even if the mount is shadowed. + // I don't know whether this is a bug in gvfs or not. + // So let's check if its in our list instead. + if(pThis->shadowedMounts_.removeOne(mount)) { + // if this is a shadowed mount + // qDebug() << "remove shadow mount"; + g_object_unref(mount); + } +#endif +} + +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() << 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(placesRoot->child(row)); + item->updateIcon(); + } + n = devicesRoot->rowCount(); + for(row = 0; row < n; ++row) { + item = static_cast(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(g_list_nth_data(allBookmarks, oldPos)); + // If we cannot find the dragged bookmark item at position , 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(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(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(); +} + + +} // namespace Fm diff --git a/src/placesmodel.h b/src/placesmodel.h new file mode 100644 index 0000000..9029590 --- /dev/null +++ b/src/placesmodel.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_PLACESMODEL_H +#define FM_PLACESMODEL_H + +#include "libfmqtglobals.h" +#include +#include +#include +#include +#include + +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 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_; + QList shadowedMounts_; +}; + +} + +#endif // FM_PLACESMODEL_H diff --git a/src/placesmodelitem.cpp b/src/placesmodelitem.cpp new file mode 100644 index 0000000..8df7608 --- /dev/null +++ b/src/placesmodelitem.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "placesmodelitem.h" +#include "icontheme.h" +#include + +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(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(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); +} + +} diff --git a/src/placesmodelitem.h b/src/placesmodelitem.h new file mode 100644 index 0000000..d788bbb --- /dev/null +++ b/src/placesmodelitem.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_PLACESMODELITEM_H +#define FM_PLACESMODELITEM_H + +#include "libfmqtglobals.h" +#include +#include +#include +#include +#include + +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 diff --git a/src/placesview.cpp b/src/placesview.cpp new file mode 100644 index 0000000..9d6f610 --- /dev/null +++ b/src/placesview.cpp @@ -0,0 +1,425 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "placesview.h" +#include "placesmodel.h" +#include "placesmodelitem.h" +#include "mountoperation.h" +#include "fileoperation.h" +#include +#include +#include +#include +#include + +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(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(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(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(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(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(sender()); + if(!action->index().isValid()) + return; + PlacesModelBookmarkItem* item = static_cast(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(sender()); + if(!action->index().isValid()) + return; + PlacesModelBookmarkItem* item = static_cast(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(sender()); + if(!action->index().isValid()) + return; + PlacesModelBookmarkItem* item = static_cast(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(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(sender()); + if(!action->index().isValid()) + return; + PlacesModelItem* item = static_cast(model_->itemFromIndex(action->index())); + if(item) + Q_EMIT chdirRequested(1, item->path()); +} + +void PlacesView::onOpenNewWindow() +{ + PlacesModel::ItemAction* action = static_cast(sender()); + if(!action->index().isValid()) + return; + PlacesModelItem* item = static_cast(model_->itemFromIndex(action->index())); + if(item) + Q_EMIT chdirRequested(2, item->path()); +} + +void PlacesView::onRenameBookmark() { + PlacesModel::ItemAction* action = static_cast(sender()); + if(!action->index().isValid()) + return; + PlacesModelBookmarkItem* item = static_cast(model_->itemFromIndex(action->index())); + setFocus(); + setCurrentIndex(item->index()); + edit(item->index()); +} + +void PlacesView::onMountVolume() { + PlacesModel::ItemAction* action = static_cast(sender()); + if(!action->index().isValid()) + return; + PlacesModelVolumeItem* item = static_cast(model_->itemFromIndex(action->index())); + MountOperation* op = new MountOperation(true, this); + op->mount(item->volume()); + op->wait(); +} + +void PlacesView::onUnmountVolume() { + PlacesModel::ItemAction* action = static_cast(sender()); + if(!action->index().isValid()) + return; + PlacesModelVolumeItem* item = static_cast(model_->itemFromIndex(action->index())); + MountOperation* op = new MountOperation(true, this); + op->unmount(item->volume()); + op->wait(); +} + +void PlacesView::onUnmountMount() { + PlacesModel::ItemAction* action = static_cast(sender()); + if(!action->index().isValid()) + return; + PlacesModelMountItem* item = static_cast(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(sender()); + if(!action->index().isValid()) + return; + PlacesModelVolumeItem* item = static_cast(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); + + // Do not take the ownership of the menu since + // it will be deleted with deleteLater() upon hidden. + // This is possibly related to #145 - https://github.com/lxde/pcmanfm-qt/issues/145 + QMenu* menu = new QMenu(); + QAction* action; + PlacesModelItem* item = static_cast(model_->itemFromIndex(index)); + + if(item->type() != PlacesModelItem::Mount + && (item->type() != PlacesModelItem::Volume + || static_cast(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(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(); + } + } +} + + +} // namespace Fm diff --git a/src/placesview.h b/src/placesview.h new file mode 100644 index 0000000..902ac3d --- /dev/null +++ b/src/placesview.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2012 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_PLACESVIEW_H +#define FM_PLACESVIEW_H + +#include "libfmqtglobals.h" +#include +#include + +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 diff --git a/src/proxyfoldermodel.cpp b/src/proxyfoldermodel.cpp new file mode 100644 index 0000000..b375a2d --- /dev/null +++ b/src/proxyfoldermodel.cpp @@ -0,0 +1,281 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "proxyfoldermodel.h" +#include "foldermodel.h" +#include + +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(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(sourceModel()); + FolderModel* newSrcModel = static_cast(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(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(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(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(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(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(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(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(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 + + +} // namespace Fm diff --git a/src/proxyfoldermodel.h b/src/proxyfoldermodel.h new file mode 100644 index 0000000..ed4dec3 --- /dev/null +++ b/src/proxyfoldermodel.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_PROXYFOLDERMODEL_H +#define FM_PROXYFOLDERMODEL_H + +#include "libfmqtglobals.h" +#include +#include +#include + +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 filters_; +}; + +} + +#endif // FM_PROXYFOLDERMODEL_H diff --git a/src/rename-dialog.ui b/src/rename-dialog.ui new file mode 100644 index 0000000..2b0c123 --- /dev/null +++ b/src/rename-dialog.ui @@ -0,0 +1,204 @@ + + + RenameDialog + + + + 0 + 0 + 398 + 220 + + + + Confirm to replace files + + + false + + + + 6 + + + 10 + + + + + + 0 + 0 + + + + <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> + + + + + + + 12 + + + 6 + + + + + + 0 + 0 + + + + dest + + + + + + + with the following file? + + + + + + + + 0 + 0 + + + + src file info + + + + + + + + 0 + 0 + + + + dest file info + + + + + + + + 0 + 0 + + + + src + + + + + + + + + 12 + + + + + + 0 + 0 + + + + &File name: + + + fileName + + + + + + + + + + + + Apply this option to all existing files + + + + + + + Qt::Vertical + + + QSizePolicy::Expanding + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ignore|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + RenameDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + RenameDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/src/renamedialog.cpp b/src/renamedialog.cpp new file mode 100644 index 0000000..e9f9456 --- /dev/null +++ b/src/renamedialog.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "renamedialog.h" +#include "ui_rename-dialog.h" +#include +#include +#include "icontheme.h" + +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(ui->buttonBox->button(QDialogButtonBox::Ok)); + overwriteButton->setEnabled(!hasNewName); + overwriteButton->setDefault(!hasNewName); +} + + +} // namespace Fm diff --git a/src/renamedialog.h b/src/renamedialog.h new file mode 100644 index 0000000..f979584 --- /dev/null +++ b/src/renamedialog.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_RENAMEDIALOG_H +#define FM_RENAMEDIALOG_H + +#include "libfmqtglobals.h" +#include +#include + +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 diff --git a/src/sidepane.cpp b/src/sidepane.cpp new file mode 100644 index 0000000..9c9a20f --- /dev/null +++ b/src/sidepane.cpp @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "sidepane.h" +#include +#include +#include +#include "placesview.h" +#include "dirtreeview.h" +#include "dirtreemodel.h" +#include "path.h" +#include "filemenu.h" + +namespace Fm { + +SidePane::SidePane(QWidget* parent): + QWidget(parent), + showHidden_(false), + mode_(ModeNone), + view_(NULL), + combo_(NULL), + currentPath_(NULL), + iconSize_(24, 24) { + + verticalLayout = new QVBoxLayout(this); + verticalLayout->setContentsMargins(0, 0, 0, 0); + + combo_ = new QComboBox(this); + combo_->setFrame(false); + combo_->addItem(tr("Places")); + combo_->addItem(tr("Directory Tree")); + connect(combo_, static_cast(&QComboBox::currentIndexChanged), this, &SidePane::onComboCurrentIndexChanged); + verticalLayout->addWidget(combo_); +} + +SidePane::~SidePane() { + if(currentPath_) + fm_path_unref(currentPath_); + // qDebug("delete SidePane"); +} + +void SidePane::onPlacesViewChdirRequested(int type, FmPath* path) { + Q_EMIT chdirRequested(type, path); +} + +void SidePane::onDirTreeViewChdirRequested(int type, FmPath* path) { + Q_EMIT chdirRequested(type, path); +} + +void SidePane::onComboCurrentIndexChanged(int current) { + if(current != mode_) { + setMode(Mode(current)); + } +} + +void SidePane::setIconSize(QSize size) { + iconSize_ = size; + switch(mode_) { + case ModePlaces: + static_cast(view_)->setIconSize(size); + case ModeDirTree: + static_cast(view_)->setIconSize(size); + break; + default:; + } +} + +void SidePane::setCurrentPath(FmPath* path) { + Q_ASSERT(path != NULL); + if(currentPath_) + fm_path_unref(currentPath_); + currentPath_ = fm_path_ref(path); + switch(mode_) { + case ModePlaces: + static_cast(view_)->setCurrentPath(path); + break; + case ModeDirTree: + static_cast(view_)->setCurrentPath(path); + break; + default:; + } +} + +SidePane::Mode SidePane::modeByName(const char* str) { + if(str == NULL) + return ModeNone; + if(strcmp(str, "places") == 0) + return ModePlaces; + if(strcmp(str, "dirtree") == 0) + return ModeDirTree; + return ModeNone; +} + +const char* SidePane::modeName(SidePane::Mode mode) { + switch(mode) { + case ModePlaces: + return "places"; + case ModeDirTree: + return "dirtree"; + default: + return NULL; + } +} + +#if 0 // FIXME: are these APIs from libfm-qt needed? + +QString SidePane::modeLabel(SidePane::Mode mode) { + switch(mode) { + case ModePlaces: + return tr("Places"); + case ModeDirTree: + return tr("Directory Tree"); + } + return QString(); +} + +QString SidePane::modeTooltip(SidePane::Mode mode) { + switch(mode) { + case ModePlaces: + return tr("Shows list of common places, devices, and bookmarks in sidebar"); + case ModeDirTree: + return tr("Shows tree of directories in sidebar"); + } + return QString(); +} +#endif + +bool SidePane::setHomeDir(const char* home_dir) { + if(view_ == NULL) + return false; + // TODO: SidePane::setHomeDir + + switch(mode_) { + case ModePlaces: + // static_cast(view_); + return true; + case ModeDirTree: + // static_cast(view_); + return true; + default:; + } + return true; +} + +void SidePane::initDirTree() { + // TODO + DirTreeModel* model = new DirTreeModel(view_); + FmFileInfoJob* job = fm_file_info_job_new(NULL, FM_FILE_INFO_JOB_NONE); + model->setShowHidden(showHidden_); + + GList* l; + /* query FmFileInfo for home dir and root dir, and then, + * add them to dir tree model */ + fm_file_info_job_add(job, fm_path_get_home()); + fm_file_info_job_add(job, fm_path_get_root()); + /* FIXME: maybe it's cleaner to use run_async here? */ + fm_job_run_sync_with_mainloop(FM_JOB(job)); + for(l = fm_file_info_list_peek_head_link(job->file_infos); l; l = l->next) { + FmFileInfo* fi = FM_FILE_INFO(l->data); + model->addRoot(fi); + } + g_object_unref(job); + + static_cast(view_)->setModel(model); +} + +void SidePane::setMode(Mode mode) { + if(mode == mode_) + return; + + if(view_) { + delete view_; + view_ = NULL; + //if(sp->update_popup) + // g_signal_handlers_disconnect_by_func(sp->view, on_item_popup, sp); + } + mode_ = mode; + + combo_->setCurrentIndex(mode); + switch(mode) { + case ModePlaces: { + PlacesView* placesView = new Fm::PlacesView(this); + view_ = placesView; + placesView->setIconSize(iconSize_); + placesView->setCurrentPath(currentPath_); + connect(placesView, &PlacesView::chdirRequested, this, &SidePane::onPlacesViewChdirRequested); + break; + } + case ModeDirTree: { + DirTreeView* dirTreeView = new Fm::DirTreeView(this); + view_ = dirTreeView; + initDirTree(); + dirTreeView->setIconSize(iconSize_); + dirTreeView->setCurrentPath(currentPath_); + connect(dirTreeView, &DirTreeView::chdirRequested, this, &SidePane::onDirTreeViewChdirRequested); + connect(dirTreeView, &DirTreeView::openFolderInNewWindowRequested, + this, &SidePane::openFolderInNewWindowRequested); + connect(dirTreeView, &DirTreeView::openFolderInNewTabRequested, + this, &SidePane::openFolderInNewTabRequested); + connect(dirTreeView, &DirTreeView::openFolderInTerminalRequested, + this, &SidePane::openFolderInTerminalRequested); + connect(dirTreeView, &DirTreeView::createNewFolderRequested, + this, &SidePane::createNewFolderRequested); + connect(dirTreeView, &DirTreeView::prepareFileMenu, + this, &SidePane::prepareFileMenu); + break; + } + default:; + } + if(view_) { + // if(sp->update_popup) + // g_signal_connect(sp->view, "item-popup", G_CALLBACK(on_item_popup), sp); + verticalLayout->addWidget(view_); + } + Q_EMIT modeChanged(mode); +} + +void SidePane::setShowHidden(bool show_hidden) { + if(view_ == NULL || show_hidden == showHidden_) + return; + showHidden_ = show_hidden; + if(mode_ == ModeDirTree) { + DirTreeView* dirTreeView = static_cast(view_); + DirTreeModel* model = static_cast( dirTreeView->model()); + if(model) + model->setShowHidden(showHidden_); + } +} + +} // namespace Fm diff --git a/src/sidepane.h b/src/sidepane.h new file mode 100644 index 0000000..3657413 --- /dev/null +++ b/src/sidepane.h @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#ifndef FM_SIDEPANE_H +#define FM_SIDEPANE_H + +#include "libfmqtglobals.h" +#include +#include + +class QComboBox; +class QVBoxLayout; +class QWidget; + +namespace Fm { + +class FileMenu; + +class LIBFM_QT_API SidePane : public QWidget { + Q_OBJECT + +public: + enum Mode { + ModeNone = -1, + ModePlaces = 0, + ModeDirTree, + NumModes + }; + +public: + explicit SidePane(QWidget* parent = 0); + virtual ~SidePane(); + + QSize iconSize() { + return iconSize_; + } + + void setIconSize(QSize size); + + FmPath* currentPath() { + return currentPath_; + } + + void setCurrentPath(FmPath* path); + + void setMode(Mode mode); + + Mode mode() { + return mode_; + } + + QWidget* view() { + return view_; + } + + const char *modeName(Mode mode); + + Mode modeByName(const char *str); + +#if 0 // FIXME: are these APIs from libfm-qt needed? + int modeCount(void) { + return NumModes; + } + + QString modeLabel(Mode mode); + + QString modeTooltip(Mode mode); +#endif + + void setShowHidden(bool show_hidden); + + bool showHidden() { + return showHidden_; + } + + bool setHomeDir(const char *home_dir); + + // libfm-gtk compatible alias + FmPath* getCwd() { + return currentPath(); + } + + void chdir(FmPath* path) { + setCurrentPath(path); + } + +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 modeChanged(Fm::SidePane::Mode mode); + + void prepareFileMenu(Fm::FileMenu* menu); // emit before showing a Fm::FileMenu + +protected Q_SLOTS: + void onPlacesViewChdirRequested(int type, FmPath* path); + void onDirTreeViewChdirRequested(int type, FmPath* path); + void onComboCurrentIndexChanged(int current); + +private: + void initDirTree(); + +private: + FmPath* currentPath_; + QWidget* view_; + QComboBox* combo_; + QVBoxLayout* verticalLayout; + QSize iconSize_; + Mode mode_; + bool showHidden_; +}; + +} + +#endif // FM_SIDEPANE_H diff --git a/src/thumbnailloader.cpp b/src/thumbnailloader.cpp new file mode 100644 index 0000000..52c03a5 --- /dev/null +++ b/src/thumbnailloader.cpp @@ -0,0 +1,199 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + + +#include "thumbnailloader.h" +#include +#include + +namespace Fm { + +// FmQImageWrapper is a GObject used to wrap QImage objects and use in glib-based libfm +#define FM_TYPE_QIMAGE_WRAPPER (fm_qimage_wrapper_get_type()) +#define FM_QIMAGE_WRAPPER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\ +FM_TYPE_QIMAGE_WRAPPER, FmQImageWrapper)) +#define FM_QIMAGE_WRAPPER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),\ +FM_TYPE_QIMAGE_WRAPPER, FmQImageWrapperClass)) +#define FM_IS_QIMAGE_WRAPPER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),\ +FM_TYPE_QIMAGE_WRAPPER)) +#define FM_IS_QIMAGE_WRAPPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),\ +FM_TYPE_QIMAGE_WRAPPER)) +#define FM_QIMAGE_WRAPPER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),\ +FM_TYPE_QIMAGE_WRAPPER, FmQImageWrapperClass)) + +typedef struct _FmQImageWrapper FmQImageWrapper; +typedef struct _FmQImageWrapperClass FmQImageWrapperClass; + +struct _FmQImageWrapper { + GObject parent; + QImage image; +}; + +struct _FmQImageWrapperClass { + GObjectClass parent_class; +}; + +GType fm_qimage_wrapper_get_type(void); +GObject* fm_qimage_wrapper_new(void); +static void fm_qimage_wrapper_finalize(GObject *self); + +G_DEFINE_TYPE(FmQImageWrapper, fm_qimage_wrapper, G_TYPE_OBJECT) + +static void fm_qimage_wrapper_class_init(FmQImageWrapperClass *klass) { + GObjectClass* object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fm_qimage_wrapper_finalize; +} + +static void fm_qimage_wrapper_init(FmQImageWrapper *self) { + // placement new for QImage + new(&self->image) QImage(); +} + +static void fm_qimage_wrapper_finalize(GObject *self) { + FmQImageWrapper *wrapper = FM_QIMAGE_WRAPPER(self); + // placement delete + wrapper->image.~QImage(); +} + +GObject *fm_qimage_wrapper_new(QImage& image) { + FmQImageWrapper *wrapper = (FmQImageWrapper*)g_object_new(FM_TYPE_QIMAGE_WRAPPER, NULL); + wrapper->image = image; + return (GObject*)wrapper; +} + +ThumbnailLoader* ThumbnailLoader::theThumbnailLoader = NULL; +bool ThumbnailLoader::localFilesOnly_ = true; +int ThumbnailLoader::maxThumbnailFileSize_ = 0; + +ThumbnailLoader::ThumbnailLoader() { + // apply the settings to libfm + fm_config->thumbnail_local = localFilesOnly_; + fm_config->thumbnail_max = maxThumbnailFileSize_; + + FmThumbnailLoaderBackend qt_backend = { + readImageFromFile, + readImageFromStream, + writeImage, + scaleImage, + rotateImage, + getImageWidth, + getImageHeight, + getImageText, + setImageText + }; + gboolean success = fm_thumbnail_loader_set_backend(&qt_backend); +} + +ThumbnailLoader::~ThumbnailLoader() { + +} + +GObject* ThumbnailLoader::readImageFromFile(const char* filename) { + QImage image; + image.load(QString(filename)); + // qDebug("readImageFromFile: %s, %d", filename, image.isNull()); + return image.isNull() ? NULL : fm_qimage_wrapper_new(image); +} + +GObject* ThumbnailLoader::readImageFromStream(GInputStream* stream, guint64 len, GCancellable* cancellable) { + // qDebug("readImageFromStream: %p, %llu", stream, len); + // FIXME: should we set a limit here? Otherwise if len is too large, we can run out of memory. + unsigned char* buffer = new unsigned char[len]; // allocate enough buffer + unsigned char* pbuffer = buffer; + int totalReadSize = 0; + while(!g_cancellable_is_cancelled(cancellable) && totalReadSize < len) { + int bytesToRead = totalReadSize + 4096 > len ? len - totalReadSize : 4096; + gssize readSize = g_input_stream_read(stream, pbuffer, bytesToRead, cancellable, NULL); + if(readSize == 0) // end of file + break; + else if(readSize == -1) // error + return NULL; + totalReadSize += readSize; + pbuffer += readSize; + } + QImage image; + image.loadFromData(buffer, totalReadSize); + delete []buffer; + return image.isNull() ? NULL : fm_qimage_wrapper_new(image); +} + +gboolean ThumbnailLoader::writeImage(GObject* image, const char* filename) { + FmQImageWrapper* wrapper = FM_QIMAGE_WRAPPER(image); + if(wrapper == NULL || wrapper->image.isNull()) + return FALSE; + return (gboolean)wrapper->image.save(filename, "PNG"); +} + +GObject* ThumbnailLoader::scaleImage(GObject* ori_pix, int new_width, int new_height) { + // qDebug("scaleImage: %d, %d", new_width, new_height); + FmQImageWrapper* ori_wrapper = FM_QIMAGE_WRAPPER(ori_pix); + QImage scaled = ori_wrapper->image.scaled(new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); + return scaled.isNull() ? NULL : fm_qimage_wrapper_new(scaled); +} + +GObject* ThumbnailLoader::rotateImage(GObject* image, int degree) { + FmQImageWrapper* wrapper = FM_QIMAGE_WRAPPER(image); + // degree values are 0, 90, 180, and 270 counterclockwise. + // In Qt, QMatrix does rotation counterclockwise as well. + // However, because the y axis of widget coordinate system is downward, + // the real effect of the coordinate transformation becomes clockwise rotation. + // So we need to use (360 - degree) here. + // Quote from QMatrix API doc: + // Note that if you apply a QMatrix to a point defined in widget + // coordinates, the direction of the rotation will be clockwise because + // the y-axis points downwards. + QImage rotated = wrapper->image.transformed(QMatrix().rotate(360 - degree)); + return rotated.isNull() ? NULL : fm_qimage_wrapper_new(rotated); +} + +int ThumbnailLoader::getImageWidth(GObject* image) { + FmQImageWrapper* wrapper = FM_QIMAGE_WRAPPER(image); + return wrapper->image.width(); +} + +int ThumbnailLoader::getImageHeight(GObject* image) { + FmQImageWrapper* wrapper = FM_QIMAGE_WRAPPER(image); + return wrapper->image.height(); +} + +char* ThumbnailLoader::getImageText(GObject* image, const char* key) { + FmQImageWrapper* wrapper = FM_QIMAGE_WRAPPER(image); + QByteArray text = wrapper->image.text(key).toLatin1(); + return (char*)g_memdup(text.constData(), text.length()); +} + +gboolean ThumbnailLoader::setImageText(GObject* image, const char* key, const char* val) { + FmQImageWrapper* wrapper = FM_QIMAGE_WRAPPER(image); + // NOTE: we might receive image=NULL sometimes with older versions of libfm. + if(Q_LIKELY(wrapper != NULL)) { + wrapper->image.setText(key, val); + } + return TRUE; +} + +QImage ThumbnailLoader::image(FmThumbnailLoader* result) { + FmQImageWrapper* wrapper = FM_QIMAGE_WRAPPER(fm_thumbnail_loader_get_data(result)); + if(wrapper) { + return wrapper->image; + } + return QImage(); +} + + +} // namespace Fm diff --git a/src/thumbnailloader.h b/src/thumbnailloader.h new file mode 100644 index 0000000..37a8960 --- /dev/null +++ b/src/thumbnailloader.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef FM_THUMBNAILLOADER_H +#define FM_THUMBNAILLOADER_H + +#include "libfmqtglobals.h" +#include +#include +#include + +namespace Fm { + +class LIBFM_QT_API ThumbnailLoader { + +public: + ThumbnailLoader(); + virtual ~ThumbnailLoader(); + + static ThumbnailLoader* instance() { + return theThumbnailLoader; + } + + static FmThumbnailLoader* load(FmFileInfo* fileInfo, int size, FmThumbnailLoaderCallback callback, gpointer user_data) { + // qDebug("load thumbnail: %s", fm_file_info_get_disp_name(fileInfo)); + return fm_thumbnail_loader_load(fileInfo, size, callback, user_data); + } + + static FmFileInfo* fileInfo(FmThumbnailLoader* result) { + return fm_thumbnail_loader_get_file_info(result); + } + + static void cancel(FmThumbnailLoader* result) { + fm_thumbnail_loader_cancel(result); + } + + static QImage image(FmThumbnailLoader* result); + + static int size(FmThumbnailLoader* result) { + return fm_thumbnail_loader_get_size(result); + } + + static void setLocalFilesOnly(bool value) { + localFilesOnly_ = value; + if(fm_config) + fm_config->thumbnail_local = localFilesOnly_; + } + + static bool localFilesOnly() { + return localFilesOnly_; + } + + static int maxThumbnailFileSize() { + return maxThumbnailFileSize_; + } + + static void setMaxThumbnailFileSize(int size) { + maxThumbnailFileSize_ = size; + if(fm_config) + fm_config->thumbnail_max = maxThumbnailFileSize_; + } + +private: + static GObject* readImageFromFile(const char* filename); + static GObject* readImageFromStream(GInputStream* stream, guint64 len, GCancellable* cancellable); + static gboolean writeImage(GObject* image, const char* filename); + static GObject* scaleImage(GObject* ori_pix, int new_width, int new_height); + static int getImageWidth(GObject* image); + static int getImageHeight(GObject* image); + static char* getImageText(GObject* image, const char* key); + static gboolean setImageText(GObject* image, const char* key, const char* val); + static GObject* rotateImage(GObject* image, int degree); + +private: + static ThumbnailLoader* theThumbnailLoader; + static bool localFilesOnly_; + static int maxThumbnailFileSize_; +}; + +} + +#endif // FM_THUMBNAILLOADER_H diff --git a/src/translations/libfm-qt.ts b/src/translations/libfm-qt.ts new file mode 100644 index 0000000..d04b96e --- /dev/null +++ b/src/translations/libfm-qt.ts @@ -0,0 +1,1265 @@ + + + + + AppChooserDialog + + + Choose an Application + + + + + Installed Applications + + + + + Custom Command + + + + + Command line to execute: + + + + + Application name: + + + + + <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> + + + + + Keep terminal window open after command execution + + + + + Execute in terminal emulator + + + + + Set selected application as default action of this file type + + + + + EditBookmarksDialog + + + Edit Bookmarks + + + + + Name + + + + + Location + + + + + &Add Item + + + + + &Remove Item + + + + + Use drag and drop to reorder the items + + + + + ExecFileDialog + + + Execute file + + + + + &Open + + + + + E&xecute + + + + + Execute in &Terminal + + + + + Cancel + + + + + FileOperationDialog + + + Destination: + + + + + Processing: + + + + + Preparing... + + + + + Progress + + + + + Time remaining: + + + + + FilePropsDialog + + + File Properties + + + + + General + + + + + Location: + + + + + File type: + + + + + Mime type: + + + + + File size: + + + + + On-disk size: + + + + + Last modified: + + + + + Link target: + + + + + Open With: + + + + + Last accessed: + + + + + Permissions + + + + + Ownership + + + + + + + Group: + + + + + + + Owner: + + + + + Access Control + + + + + + Other: + + + + + Make the file executable + + + + + + + Read + + + + + + + Write + + + + + + + Execute + + + + + Sticky + + + + + SetUID + + + + + SetGID + + + + + Advanced Mode + + + + + Fm::AppChooserComboBox + + + Customize + + + + + Fm::AppChooserDialog + + + Select an application to open "%1" files + + + + + Fm::CreateNewMenu + + + Folder + + + + + Blank File + + + + + Fm::DirTreeModel + + + Loading... + + + + + <No sub folders> + + + + + Fm::DirTreeView + + + Open in New T&ab + + + + + Open in New Win&dow + + + + + Open in Termina&l + + + + + Fm::DndActionMenu + + + Copy here + + + + + Move here + + + + + Create symlink here + + + + + Cancel + + + + + Fm::EditBookmarksDialog + + + New bookmark + + + + + Fm::ExecFileDialog + + + This text file '%1' seems to be an executable script. +What do you want to do with it? + + + + + This file '%1' is executable. Do you want to execute it? + + + + + Fm::FileMenu + + + Open + + + + + Open With... + + + + + Other Applications + + + + + Create &New + + + + + &Restore + + + + + Cut + + + + + Copy + + + + + Paste + + + + + + &Move to Trash + + + + + Rename + + + + + Extract to... + + + + + Extract Here + + + + + Compress + + + + + Properties + + + + + Output + + + + + &Delete + + + + + Fm::FileOperation + + + Error + + + + + Some files cannot be moved to trash can because the underlying file systems don't support this operation. +Do you want to delete them instead? + + + + + + Confirm + + + + + Do you want to delete the selected files? + + + + + Do you want to move the selected files to trash can? + + + + + Fm::FileOperationDialog + + + Move files + + + + + Moving the following files to destination folder: + + + + + Copy Files + + + + + Copying the following files to destination folder: + + + + + Trash Files + + + + + Moving the following files to trash can: + + + + + Delete Files + + + + + Deleting the following files + + + + + Create Symlinks + + + + + Creating symlinks for the following files: + + + + + Change Attributes + + + + + Changing attributes of the following files: + + + + + Restore Trashed Files + + + + + Restoring the following files from trash can: + + + + + Error + + + + + Fm::FilePropsDialog + + + View folder content + + + + + View and modify folder content + + + + + Read + + + + + Read and write + + + + + Forbidden + + + + + Files of different types + + + + + Multiple Files + + + + + Apply changes + + + + + Do you want to recursively apply these changes to all files and sub-folders? + + + + + Fm::FileSearchDialog + + + Error + + + + + You should add at least add one directory to search. + + + + + Select a folder + + + + + Fm::FolderMenu + + + Create &New + + + + + &Paste + + + + + Select &All + + + + + Invert Selection + + + + + Sorting + + + + + Show Hidden + + + + + Folder Pr&operties + + + + + By File Name + + + + + By Modification Time + + + + + By File Size + + + + + By File Type + + + + + By File Owner + + + + + Ascending + + + + + Descending + + + + + Folder First + + + + + Case Sensitive + + + + + Fm::FolderModel + + + Name + + + + + Type + + + + + Size + + + + + Modified + + + + + Owner + + + + + Fm::FontButton + + + Bold + + + + + Italic + + + + + Fm::MountOperationPasswordDialog + + + &Connect + + + + + Fm::PlacesModel + + + Places + + + + + Desktop + + + + + Computer + + + + + Applications + + + + + Network + + + + + Devices + + + + + Bookmarks + + + + + Trash + + + + + Fm::PlacesView + + + Open in New Tab + + + + + Open in New Window + + + + + Empty Trash + + + + + Move Bookmark Up + + + + + Move Bookmark Down + + + + + Rename Bookmark + + + + + Remove Bookmark + + + + + + Unmount + + + + + Mount + + + + + Eject + + + + + Fm::RenameDialog + + + + Type: %1 +Size: %2 +Modified: %3 + + + + + Type: %1 +Modified: %2 + + + + + Type: %1 +Modified: %3 + + + + + &Overwrite + + + + + &Rename + + + + + Fm::SidePane + + + + Places + + + + + + Directory Tree + + + + + Shows list of common places, devices, and bookmarks in sidebar + + + + + Shows tree of directories in sidebar + + + + + MountOperationPasswordDialog + + + Mount + + + + + Connect &anonymously + + + + + Connect as u&ser: + + + + + &Username: + + + + + &Password: + + + + + &Domain: + + + + + Forget password &immediately + + + + + Remember password until you &logout + + + + + Remember &forever + + + + + QObject + + + Rename File + + + + + Please enter a new name: + + + + + + + + Error + + + + + Create Folder + + + + + Create File + + + + + Please enter a new file name: + + + + + New text file + + + + + Please enter a new folder name: + + + + + New folder + + + + + Enter a name for the new %1: + + + + + RenameDialog + + + Confirm to replace files + + + + + <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> + + + + + dest + + + + + with the following file? + + + + + src file info + + + + + dest file info + + + + + src + + + + + &File name: + + + + + Apply this option to all existing files + + + + + SearchDialog + + + Search Files + + + + + Name/Location + + + + + File Name Patterns: + + + + + * + + + + + Case insensitive + + + + + Use regular expression + + + + + Places to Search: + + + + + &Add + + + + + &Remove + + + + + Search in sub directories + + + + + Search for hidden files + + + + + File Type + + + + + Only search for files of following types: + + + + + Text files + + + + + Image files + + + + + Audio files + + + + + Video files + + + + + Documents + + + + + Folders + + + + + Content + + + + + File contains: + + + + + Case insensiti&ve + + + + + &Use regular expression + + + + + Properties + + + + + File Size: + + + + + Larger than: + + + + + + Bytes + + + + + + KiB + + + + + + MiB + + + + + + GiB + + + + + Smaller than: + + + + + Last Modified Time: + + + + + Earlier than: + + + + + Later than: + + + + diff --git a/src/translations/libfm-qt_ar.ts b/src/translations/libfm-qt_ar.ts new file mode 100644 index 0000000..81c124b --- /dev/null +++ b/src/translations/libfm-qt_ar.ts @@ -0,0 +1,1265 @@ + + + + + AppChooserDialog + + + Choose an Application + + + + + Installed Applications + + + + + Custom Command + + + + + Command line to execute: + + + + + Application name: + + + + + <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> + + + + + Keep terminal window open after command execution + + + + + Execute in terminal emulator + + + + + Set selected application as default action of this file type + + + + + EditBookmarksDialog + + + Edit Bookmarks + + + + + Name + + + + + Location + + + + + &Add Item + + + + + &Remove Item + + + + + Use drag and drop to reorder the items + + + + + ExecFileDialog + + + Execute file + + + + + &Open + + + + + E&xecute + + + + + Execute in &Terminal + + + + + Cancel + + + + + FileOperationDialog + + + Destination: + + + + + Processing: + + + + + Preparing... + + + + + Progress + + + + + Time remaining: + + + + + FilePropsDialog + + + File Properties + + + + + General + + + + + Location: + + + + + File type: + + + + + Mime type: + + + + + File size: + + + + + On-disk size: + + + + + Last modified: + + + + + Link target: + + + + + Open With: + + + + + Last accessed: + + + + + Permissions + + + + + Ownership + + + + + + + Group: + + + + + + + Owner: + + + + + Access Control + + + + + + Other: + + + + + Make the file executable + + + + + + + Read + + + + + + + Write + + + + + + + Execute + + + + + Sticky + + + + + SetUID + + + + + SetGID + + + + + Advanced Mode + + + + + Fm::AppChooserComboBox + + + Customize + + + + + Fm::AppChooserDialog + + + Select an application to open "%1" files + + + + + Fm::CreateNewMenu + + + Folder + + + + + Blank File + + + + + Fm::DirTreeModel + + + Loading... + + + + + <No sub folders> + + + + + Fm::DirTreeView + + + Open in New T&ab + + + + + Open in New Win&dow + + + + + Open in Termina&l + + + + + Fm::DndActionMenu + + + Copy here + + + + + Move here + + + + + Create symlink here + + + + + Cancel + + + + + Fm::EditBookmarksDialog + + + New bookmark + + + + + Fm::ExecFileDialog + + + This text file '%1' seems to be an executable script. +What do you want to do with it? + + + + + This file '%1' is executable. Do you want to execute it? + + + + + Fm::FileMenu + + + Open + + + + + Cut + + + + + Copy + + + + + Paste + + + + + + &Move to Trash + + + + + Output + + + + + &Delete + + + + + Rename + + + + + Open With... + + + + + Other Applications + + + + + Create &New + + + + + &Restore + + + + + Extract to... + + + + + Extract Here + + + + + Compress + + + + + Properties + + + + + Fm::FileOperation + + + Error + + + + + Some files cannot be moved to trash can because the underlying file systems don't support this operation. +Do you want to delete them instead? + + + + + + Confirm + + + + + Do you want to delete the selected files? + + + + + Do you want to move the selected files to trash can? + + + + + Fm::FileOperationDialog + + + Move files + + + + + Moving the following files to destination folder: + + + + + Copy Files + + + + + Copying the following files to destination folder: + + + + + Trash Files + + + + + Moving the following files to trash can: + + + + + Delete Files + + + + + Deleting the following files + + + + + Create Symlinks + + + + + Creating symlinks for the following files: + + + + + Change Attributes + + + + + Changing attributes of the following files: + + + + + Restore Trashed Files + + + + + Restoring the following files from trash can: + + + + + Error + + + + + Fm::FilePropsDialog + + + View folder content + + + + + View and modify folder content + + + + + Read + + + + + Read and write + + + + + Forbidden + + + + + Files of different types + + + + + Multiple Files + + + + + Apply changes + + + + + Do you want to recursively apply these changes to all files and sub-folders? + + + + + Fm::FileSearchDialog + + + Error + + + + + You should add at least add one directory to search. + + + + + Select a folder + + + + + Fm::FolderMenu + + + Create &New + + + + + &Paste + + + + + Select &All + + + + + Invert Selection + + + + + Sorting + + + + + Show Hidden + + + + + Folder Pr&operties + + + + + By File Name + + + + + By Modification Time + + + + + By File Size + + + + + By File Type + + + + + By File Owner + + + + + Ascending + + + + + Descending + + + + + Folder First + + + + + Case Sensitive + + + + + Fm::FolderModel + + + Name + + + + + Type + + + + + Size + + + + + Modified + + + + + Owner + + + + + Fm::FontButton + + + Bold + + + + + Italic + + + + + Fm::MountOperationPasswordDialog + + + &Connect + + + + + Fm::PlacesModel + + + Places + + + + + Desktop + + + + + Trash + + + + + Computer + + + + + Applications + + + + + Network + + + + + Devices + + + + + Bookmarks + + + + + Fm::PlacesView + + + Empty Trash + + + + + Open in New Tab + + + + + Open in New Window + + + + + Move Bookmark Up + + + + + Move Bookmark Down + + + + + Rename Bookmark + + + + + Remove Bookmark + + + + + + Unmount + + + + + Mount + + + + + Eject + + + + + Fm::RenameDialog + + + + Type: %1 +Size: %2 +Modified: %3 + + + + + Type: %1 +Modified: %2 + + + + + Type: %1 +Modified: %3 + + + + + &Overwrite + + + + + &Rename + + + + + Fm::SidePane + + + + Places + + + + + + Directory Tree + + + + + Shows list of common places, devices, and bookmarks in sidebar + + + + + Shows tree of directories in sidebar + + + + + MountOperationPasswordDialog + + + Mount + + + + + Connect &anonymously + + + + + Connect as u&ser: + + + + + &Username: + + + + + &Password: + + + + + &Domain: + + + + + Forget password &immediately + + + + + Remember password until you &logout + + + + + Remember &forever + + + + + QObject + + + + + + Error + + + + + Rename File + + + + + Please enter a new name: + + + + + Create Folder + + + + + Please enter a new file name: + + + + + New text file + + + + + Please enter a new folder name: + + + + + New folder + + + + + Enter a name for the new %1: + + + + + Create File + + + + + RenameDialog + + + Confirm to replace files + + + + + <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> + + + + + dest + + + + + with the following file? + + + + + src file info + + + + + dest file info + + + + + src + + + + + &File name: + + + + + Apply this option to all existing files + + + + + SearchDialog + + + Search Files + + + + + Name/Location + + + + + File Name Patterns: + + + + + * + + + + + Case insensitive + + + + + Use regular expression + + + + + Places to Search: + + + + + &Add + + + + + &Remove + + + + + Search in sub directories + + + + + Search for hidden files + + + + + File Type + + + + + Only search for files of following types: + + + + + Text files + + + + + Image files + + + + + Audio files + + + + + Video files + + + + + Documents + + + + + Folders + + + + + Content + + + + + File contains: + + + + + Case insensiti&ve + + + + + &Use regular expression + + + + + Properties + + + + + File Size: + + + + + Larger than: + + + + + + Bytes + + + + + + KiB + + + + + + MiB + + + + + + GiB + + + + + Smaller than: + + + + + Last Modified Time: + + + + + Earlier than: + + + + + Later than: + + + + diff --git a/src/translations/libfm-qt_cs_CZ.ts b/src/translations/libfm-qt_cs_CZ.ts new file mode 100644 index 0000000..582602d --- /dev/null +++ b/src/translations/libfm-qt_cs_CZ.ts @@ -0,0 +1,1288 @@ + + + + + AppChooserDialog + + + Choose an Application + Výběr aplikace + + + + Installed Applications + Nainstalované aplikace + + + + Custom Command + Vlastní příkaz + + + + Command line to execute: + Příkaz k vykonání: + + + + Application name: + Jméno aplikace: + + + + <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> + <b>Tyto zástupné symboly mohou být použity v příkazovém řádku:</b> +<ul> +<li><b>%f</b>: Reprezentuje jméno jednoho souboru</li> +<li><b>%F</b>: Reprezentuje jména více souborů</li> +<li><b>%u</b>: Reprezentuje URI jednoho souboru</li> +<li><b>%U</b>: Reprezentuje URI více souborů</li> +</ul> + + + + Keep terminal window open after command execution + Nechat okno terminálu otevřené + + + + Execute in terminal emulator + Spustit v emulátoru terminálu + + + + Set selected application as default action of this file type + Použít aplikaci jako výchozí pro tento typ souboru + + + + EditBookmarksDialog + + + Edit Bookmarks + Upravit záložky + + + + Name + Jméno + + + + Location + Umístění + + + + &Add Item + &Přidat položku + + + + &Remove Item + &Odebrat položku + + + + Use drag and drop to reorder the items + Přetažením uprav pořadí + + + + ExecFileDialog + + + Execute file + Spustit soubor + + + + &Open + &Otevřít + + + + E&xecute + &Spustit + + + + Execute in &Terminal + Spustit v &Terminálu + + + + Cancel + Zrušit + + + + FileOperationDialog + + + Destination: + Umístění: + + + + Processing: + Zpracování: + + + + Preparing... + Příprava... + + + + Progress + Průběh + + + + Time remaining: + Zbývající čas: + + + + FilePropsDialog + + + File Properties + Vlastnosti souboru + + + + General + Obecné + + + + Location: + Umístění: + + + + File type: + Typ souboru: + + + + Mime type: + Mime typ: + + + + File size: + Velikost souboru: + + + + On-disk size: + Zabrané místo: + + + + Last modified: + Upraveno: + + + + Link target: + Cíl odkazu: + + + + Open With: + Otevřít pomocí: + + + + Last accessed: + Poslední přístup: + + + + Permissions + Práva + + + + Ownership + Vlastnictví + + + + + + Group: + Skupina: + + + + + + Owner: + Vlastník: + + + + Access Control + Zpřístupnění + + + + + Other: + Ostatní: + + + + Make the file executable + Označit soubor jako spustitelný + + + + + + Read + Čtení + + + + + + Write + Čtení a zápis + + + + + + Execute + Spuštění + + + + Sticky + + + + + SetUID + + + + + SetGID + + + + + Advanced Mode + Pokročilý režim + + + + Fm::AppChooserComboBox + + + Customize + + + + + Fm::AppChooserDialog + + + Select an application to open "%1" files + + + + + Fm::CreateNewMenu + + + Folder + Adresář + + + + Blank File + Prázdný soubor + + + + Fm::DirTreeModel + + + Loading... + + + + + <No sub folders> + + + + + Fm::DirTreeView + + + Open in New T&ab + + + + + Open in New Win&dow + + + + + Open in Termina&l + + + + + Fm::DndActionMenu + + + Copy here + Kopírovat sem + + + + Move here + Přesunout sem + + + + Create symlink here + Vytvořit zde odkaz + + + + Cancel + Zrušit + + + + Fm::EditBookmarksDialog + + + New bookmark + Nová záložka + + + + Fm::ExecFileDialog + + + This text file '%1' seems to be an executable script. +What do you want to do with it? + + + + + This file '%1' is executable. Do you want to execute it? + + + + + Fm::FileMenu + + + Open + Otevřít + + + + Create &New + Vytvořit &nový + + + + &Restore + O&bnovit + + + + Cut + Vyjmout + + + + Copy + Kopírovat + + + + Paste + Vložit + + + + + &Move to Trash + Přesunout do &koše + + + + Output + Výstup + + + + &Delete + &Smazat + + + + Rename + Přejmenovat + + + + Open With... + Otevřít v ... + + + + Other Applications + Ostatní programy + + + + Extract to... + Rozbalit do ... + + + + Extract Here + Rozbalit sem + + + + Compress + Komprimovat + + + + Properties + Vlastnosti + + + + Fm::FileOperation + + + Error + Chyba + + + + Some files cannot be moved to trash can because the underlying file systems don't support this operation. +Do you want to delete them instead? + Některé soubory nemohou být přesunuty do koše, protože příslušné souborové systémy nepodporují tuto operaci. +Chceš je odstranit trvale? + + + + + Confirm + Potvrdit + + + + Do you want to delete the selected files? + Chceš smazat vybrané soubory? + + + + Do you want to move the selected files to trash can? + Chceš přesunout vybrané soubory do koše? + + + + Fm::FileOperationDialog + + + Move files + + + + + Moving the following files to destination folder: + + + + + Copy Files + + + + + Copying the following files to destination folder: + + + + + Trash Files + + + + + Moving the following files to trash can: + + + + + Delete Files + + + + + Deleting the following files + + + + + Create Symlinks + + + + + Creating symlinks for the following files: + + + + + Change Attributes + + + + + Changing attributes of the following files: + + + + + Restore Trashed Files + + + + + Restoring the following files from trash can: + + + + + Error + Chyba + + + + Fm::FilePropsDialog + + + View folder content + + + + + View and modify folder content + + + + + Read + Čtení + + + + Read and write + + + + + Forbidden + + + + + Files of different types + + + + + Multiple Files + + + + + Apply changes + + + + + Do you want to recursively apply these changes to all files and sub-folders? + + + + + Fm::FileSearchDialog + + + Error + Chyba + + + + You should add at least add one directory to search. + + + + + Select a folder + + + + + Fm::FolderMenu + + + Create &New + Vytvořit &nový + + + + &Paste + &Vložit + + + + Select &All + Vybr&at všechno + + + + Invert Selection + Invertovat výběr + + + + Sorting + Řadit + + + + Show Hidden + Zobrazit skryté + + + + Folder Pr&operties + Vlastnosti sl&ožky + + + Folder + Adresář + + + Blank File + Prázdný soubor + + + + By File Name + Podle jména + + + + By Modification Time + Podle času + + + + By File Size + Podle velikosti + + + + By File Type + Podle typu + + + + By File Owner + Podle vlastníka + + + + Ascending + Vzestupně + + + + Descending + sestupně + + + + Folder First + Adresáře jako první + + + + Case Sensitive + Zohlednit velikost písmen + + + + Fm::FolderModel + + + Name + Jméno + + + + Type + Typ + + + + Size + Velikost + + + + Modified + Změněno + + + + Owner + Vlastník + + + + Fm::FontButton + + + Bold + Tučné + + + + Italic + Kurzíva + + + + Fm::MountOperationPasswordDialog + + + &Connect + &Připojit + + + + Fm::PlacesModel + + + Places + Místa + + + + Desktop + Plocha + + + + Trash + Koš + + + + Computer + Počítač + + + + Applications + Aplikace + + + + Network + Síť + + + + Devices + Zařízení + + + + Bookmarks + Záložky + + + + Fm::PlacesView + + + Empty Trash + Vysypat koš + + + Rename + Přejmenovat + + + Delete + Smazat + + + + Open in New Tab + Otevřít v novém panelu + + + + Open in New Window + Otevřít v novém okně + + + + Move Bookmark Up + Přesunout záložku nahoru + + + + Move Bookmark Down + Přesunout záložku dolů + + + + Rename Bookmark + Přejmenovat záložku + + + + Remove Bookmark + Odstranit záložku + + + + + Unmount + Odpojit + + + + Mount + Připojit + + + + Eject + Vysunout + + + + Fm::RenameDialog + + + + Type: %1 +Size: %2 +Modified: %3 + + + + + Type: %1 +Modified: %2 + + + + + Type: %1 +Modified: %3 + + + + + &Overwrite + &Přepsat + + + + &Rename + Pře&jmenovat + + + + Fm::SidePane + + + + Places + Místa + + + + + Directory Tree + Strom adresářů + + + + Shows list of common places, devices, and bookmarks in sidebar + Zobrazit seznam obvyklých míst, zařízení a záložek v postranní liště + + + + Shows tree of directories in sidebar + Zobrazit strom adresářů v postranní liště + + + + MountOperationPasswordDialog + + + Mount + Připojit + + + + Connect &anonymously + Připojit &anonymně + + + + Connect as u&ser: + Připojit jako &uživatel: + + + + &Username: + Uživatelské &jméno: + + + + &Password: + &Heslo: + + + + &Domain: + &Doména: + + + + Forget password &immediately + &Zapomenout heslo + + + + Remember password until you &logout + Pamatovat si heslo do &odhlášení + + + + Remember &forever + Pamatovat si heslo &trvale + + + + QObject + + + + + + Error + Chyba + + + + Rename File + Přejmenovat soubor + + + + Please enter a new name: + Prosím zadej nové jméno: + + + + Create Folder + Vytvořit adresář + + + + Please enter a new file name: + Prosím zadej nové jméno souboru: + + + + New text file + Nový textový soubor + + + + Please enter a new folder name: + Prosím zadej nové jméno adresáře: + + + + New folder + Nový adresář + + + + Enter a name for the new %1: + Zadej jméno pro nový %1: + + + + Create File + Vytvořit soubor + + + + RenameDialog + + + Confirm to replace files + Potvrdit náhradu souborů + + + + <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> + <html><head/><body><p><span style=" font-weight:600;">Na tomto místě už je soubor se stejným jménem.</span></p><p>Chceš nahradit existující soubor</p></body></html> + + + + dest + + + + + with the following file? + následujícím souborem? + + + + src file info + + + + + dest file info + + + + + src + + + + + &File name: + &Jméno souboru: + + + + Apply this option to all existing files + Použij tuto volbu pro všechny soubory + + + + SearchDialog + + + Search Files + + + + + Name/Location + + + + + File Name Patterns: + + + + + * + + + + + Case insensitive + + + + + Use regular expression + + + + + Places to Search: + + + + + &Add + + + + + &Remove + + + + + Search in sub directories + + + + + Search for hidden files + + + + + File Type + + + + + Only search for files of following types: + + + + + Text files + + + + + Image files + + + + + Audio files + + + + + Video files + + + + + Documents + + + + + Folders + + + + + Content + + + + + File contains: + + + + + Case insensiti&ve + + + + + &Use regular expression + + + + + Properties + Vlastnosti + + + + File Size: + + + + + Larger than: + + + + + + Bytes + + + + + + KiB + + + + + + MiB + + + + + + GiB + + + + + Smaller than: + + + + + Last Modified Time: + + + + + Earlier than: + + + + + Later than: + + + + diff --git a/src/translations/libfm-qt_de.ts b/src/translations/libfm-qt_de.ts new file mode 100644 index 0000000..6c337fe --- /dev/null +++ b/src/translations/libfm-qt_de.ts @@ -0,0 +1,1293 @@ + + + + + AppChooserDialog + + + Choose an Application + Wählen Sie eine Anwendung + + + + Installed Applications + Installierte Anwendungen + + + + Custom Command + Benutzerdefinierter Befehl + + + + Command line to execute: + Auszuführender Befehl: + + + + Application name: + Anwendungsname: + + + + <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> + <b>Diese speziellen Kürzel können im Befehl verwendet werden</b> +<ul> +<li><b>%f</b>: Repräsentiert eine einzelne Datei</li> +<li><b>%F</b>: Repräsentiert mehrere Dateien</li> +<li><b>%u</b>: Repräsentiert eine einzelne URI einer Datei</li> +<li><b>%U</b>: Repräsentiert mehrere URIs</li> +</ul> + + + + Keep terminal window open after command execution + Terminalfenster nach der Ausführung des Befehls offen lassen + + + + Execute in terminal emulator + In einem Terminalemulator ausführen + + + + Set selected application as default action of this file type + Ausgewählte Anwendung als Standardaktion für diesen Dateityp festlegen + + + + EditBookmarksDialog + + + Edit Bookmarks + Lesezeichen bearbeiten + + + + Name + Name + + + + Location + Ort + + + + &Add Item + Element &hinzufügen + + + + &Remove Item + Element &entfernen + + + + Use drag and drop to reorder the items + Klicken und ziehen Sie, um Elemente zu sortieren + + + + ExecFileDialog + + + Execute file + Datei ausführen + + + + &Open + Ö&ffnen + + + + E&xecute + &Ausführen + + + + Execute in &Terminal + In einem &Terminal ausführen + + + + Cancel + Abbrechen + + + + FileOperationDialog + + + Destination: + Ziel: + + + + Processing: + Verarbeitet: + + + + Preparing... + Vorbereiten... + + + + Progress + Fortschritt + + + + Time remaining: + Verbleibende Zeit: + + + + FilePropsDialog + + + File Properties + Dateieigenschaften + + + + General + Allgemeines + + + + Location: + Ort: + + + + File type: + Dateityp: + + + + Mime type: + MIME-Typ: + + + + File size: + Dateigröße: + + + + On-disk size: + Größe auf dem Datenträger: + + + + Last modified: + Letztes Änderungsdatum: + + + + Link target: + Verknüpfungsziel: + + + + Open With: + Öffnen mit: + + + + Last accessed: + Letzter Zugriff: + + + + Permissions + Berechtigungen + + + + Ownership + Besitz + + + + + + Group: + Gruppe: + + + + + + Owner: + Besitzer: + + + + Access Control + Zugriffskontrolle + + + + + Other: + Andere: + + + + Make the file executable + Datei ausführbar machen + + + + + + Read + Lesen + + + + + + Write + Schreiben + + + + + + Execute + Ausführen + + + + Sticky + "Sticky bit" + + + + SetUID + SetUID + + + + SetGID + SetGID + + + + Advanced Mode + Erweiterte Einstellungen + + + + Fm::AppChooserComboBox + + + Customize + Anpassen + + + + Fm::AppChooserDialog + + + Select an application to open "%1" files + Wählen Sie eine Anwendung für Dateien vom Typ "%1" + + + + Fm::CreateNewMenu + + + Folder + Ordner + + + + Blank File + Leere Datei + + + + Fm::DirTreeModel + + + Loading... + Lädt... + + + + <No sub folders> + <Keine Unterverzeichnisse> + + + + Fm::DirTreeView + + + Open in New T&ab + Öffnen in neuem T&ab + + + + Open in New Win&dow + Öffnen in neuem &Fenster + + + + Open in Termina&l + Öffnen in Termina&l + + + + Fm::DndActionMenu + + + Copy here + Hierhin kopieren + + + + Move here + Hierhin verschieben + + + + Create symlink here + Hier eine symbolische Verknüpfung erstellen + + + + Cancel + Abbrechen + + + + Fm::EditBookmarksDialog + + + New bookmark + Neues Lesezeichen + + + + Fm::ExecFileDialog + + + This text file '%1' seems to be an executable script. +What do you want to do with it? + Die Textdatei '%1' scheint ein ausführbares Skript zu sein. +Was möchten Sie damit tun? + + + + This file '%1' is executable. Do you want to execute it? + Die Datei '%1' ist ausführbar. Möchten Sie sie ausführen? + + + + Fm::FileMenu + + + Open + Öffnen + + + + Create &New + &Neu erstellen + + + + &Restore + Wiede&rherstellen + + + + Cut + Ausschneiden + + + + Copy + Kopieren + + + + Paste + Einfügen + + + + + &Move to Trash + In den &Papierkorb verschieben + + + + Output + Ausgabe + + + + &Delete + &Löschen + + + + Rename + Umbenennen + + + + Open With... + Öffnen mit... + + + + Other Applications + Andere Anwendungen + + + + Extract to... + Entpacken nach... + + + + Extract Here + Hier entpacken + + + + Compress + Komprimieren + + + + Properties + Eigenschaften + + + + Fm::FileOperation + + + Error + Fehler + + + + Some files cannot be moved to trash can because the underlying file systems don't support this operation. +Do you want to delete them instead? + Einige Dateien können nicht in den Papierkorb verschoben werden, da die zugrundeliegenden Dateisysteme den Vorgang nicht unterstützen. +Sollen die Dateien stattdessen gelöscht werden? + + + + + Confirm + Bestätigung + + + + Do you want to delete the selected files? + Möchten Sie die ausgewählten Dateien löschen? + + + + Do you want to move the selected files to trash can? + Möchten Sie die ausgewählten Dateien in den Papierkorb verschieben? + + + + Fm::FileOperationDialog + + + Move files + Dateien verschieben + + + + Moving the following files to destination folder: + Verschiebe die folgenden Dateien in den Zielordner: + + + + Copy Files + Dateien kopieren + + + + Copying the following files to destination folder: + Kopiere die folgenden Dateien in den Zielordner: + + + + Trash Files + Dateien für den Papierkorb + + + + Moving the following files to trash can: + Verschiebe die folgenden Dateien in den Papierkorb: + + + + Delete Files + Dateien löschen + + + + Deleting the following files + Lösche die folgenden Dateien + + + + Create Symlinks + Symbolische Verknüpfung erstellen + + + + Creating symlinks for the following files: + Symbolische Verknüpfungen für die folgenden Dateien erstellen: + + + + Change Attributes + Eigenschaften ändern + + + + Changing attributes of the following files: + Eigenschaften der folgenden Dateien ändern: + + + + Restore Trashed Files + Dateien aus dem Papierkorb wiederherstellen + + + + Restoring the following files from trash can: + Folgende Dateien aus dem Papierkorb wiederherstellen: + + + + Error + Fehler + + + + Fm::FilePropsDialog + + + View folder content + Ordnerinhalt ansehen + + + + View and modify folder content + Ordnerinhalt ansehen und modifizieren + + + + Read + Lesen + + + + Read and write + Lesen und schreiben + + + + Forbidden + Unzulässig + + + + Files of different types + Dateien unterschiedlicher Typen + + + + Multiple Files + Mehrere Dateien + + + + Apply changes + Änderungen anwenden + + + + Do you want to recursively apply these changes to all files and sub-folders? + Möchten Sie die Änderungen auf alle Dateien und Unterverzeichnisse anwenden? + + + + Fm::FileSearchDialog + + + Error + Fehler + + + + You should add at least add one directory to search. + + + + + Select a folder + + + + + Fm::FolderMenu + + + Create &New + &Neu erstellen + + + + &Paste + &Einfügen + + + + Select &All + &Alle auswählen + + + + Invert Selection + Auswahl umkehren + + + + Sorting + Sortierung + + + + Show Hidden + Versteckte anzeigen + + + + Folder Pr&operties + Ordner&eigenschaften + + + Folder + Ordner + + + Blank File + Leere Datei + + + + By File Name + Nach Dateiname + + + + By Modification Time + Nach Änderungsdatum + + + + By File Size + Nach Dateigröße + + + + By File Type + Nach Typ + + + + By File Owner + Nach Besitzer + + + + Ascending + Aufsteigend + + + + Descending + Absteigend + + + + Folder First + Ordner zuerst + + + + Case Sensitive + Groß-/ Kleinschreibung beachten + + + + Fm::FolderModel + + + Name + Name + + + + Type + Typ + + + + Size + Größe + + + + Modified + Geändert + + + + Owner + Besitzer + + + + Fm::FontButton + + + Bold + Fett + + + + Italic + Kursiv + + + + Fm::MountOperationPasswordDialog + + + &Connect + &Verbinden + + + + Fm::PlacesModel + + + Places + Orte + + + + Desktop + Schreibtisch + + + + Trash + Papierkorb + + + + Computer + Rechner + + + + Applications + Anwendungen + + + + Network + Netzwerk + + + + Devices + Geräte + + + + Bookmarks + Lesezeichen + + + + Fm::PlacesView + + + Empty Trash + Papierkorb leeren + + + Rename + Umbenennen + + + Delete + Löschen + + + + Open in New Tab + Öffnen in neuem Tab + + + + Open in New Window + Öffnen in neuem Fenster + + + + Move Bookmark Up + Nach oben verschieben + + + + Move Bookmark Down + Nach unten verschieben + + + + Rename Bookmark + Lesezeichen umbenennen + + + + Remove Bookmark + Lesezeichen entfernen + + + + + Unmount + Aushängen + + + + Mount + Einhängen + + + + Eject + Auswerfen + + + + Fm::RenameDialog + + + + Type: %1 +Size: %2 +Modified: %3 + Typ: %1 +Größe: %2 +Geändert: %3 + + + + Type: %1 +Modified: %2 + Typ: %1 +Geändert: %2 + + + + Type: %1 +Modified: %3 + Typ: %1 +Geändert: %3 + + + + &Overwrite + Über&schreiben + + + + &Rename + &Umbenennen + + + + Fm::SidePane + + + + Places + Orte + + + + + Directory Tree + Verzeichnisbaum + + + + Shows list of common places, devices, and bookmarks in sidebar + Zeigt eine Liste diverser Orte, Geräte und Lesezeichen in der Seitenleiste + + + + Shows tree of directories in sidebar + Zeigt einen Verzeichnisbaum in der Seitenleiste + + + + MountOperationPasswordDialog + + + Mount + Einhängen + + + + Connect &anonymously + &Anonym verbinden + + + + Connect as u&ser: + Verbinden als &Benutzer: + + + + &Username: + Benutzer&name: + + + + &Password: + &Passwort: + + + + &Domain: + &Domäne: + + + + Forget password &immediately + Passwort &sofort vergessen + + + + Remember password until you &logout + Passwort &erst beim Abmelden vergessen + + + + Remember &forever + Passwort &für immer merken + + + + QObject + + + + + + Error + Fehler + + + + Rename File + Datei umbenennen + + + + Please enter a new name: + Bitte geben Sie einen neuen Namen ein: + + + + Create Folder + Ordner erstellen + + + + Please enter a new file name: + Bitte geben Sie einen neuen Dateinamen ein: + + + + New text file + Neue Textdatei + + + + Please enter a new folder name: + Bitte geben Sie einen neuen Ordnernamen ein: + + + + New folder + Neuer Ordner + + + + Enter a name for the new %1: + Geben Sie einen Namen für %1 ein: + + + + Create File + Datei erstellen + + + + RenameDialog + + + Confirm to replace files + Überschreiben von Dateien bestätigen + + + + <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> + <html><head/><body><p><span style=" font-weight:600;">Es gibt bereits eine gleichnamige Datei an diesem Ort.</span></p><p>Möchten Sie die Datei ersetzen?</p></body></html> + + + + dest + Ziel + + + + with the following file? + Mit der folgenden Datei? + + + + src file info + Info über die Quelldatei + + + + dest file info + Info über die Zieldatei + + + + src + Quelle + + + + &File name: + &Dateiname: + + + + Apply this option to all existing files + Diese Aktion auf alle existierenden Dateien anwenden + + + + SearchDialog + + + Search Files + + + + + Name/Location + + + + + File Name Patterns: + + + + + * + + + + + Case insensitive + + + + + Use regular expression + + + + + Places to Search: + + + + + &Add + + + + + &Remove + + + + + Search in sub directories + + + + + Search for hidden files + + + + + File Type + + + + + Only search for files of following types: + + + + + Text files + + + + + Image files + + + + + Audio files + + + + + Video files + + + + + Documents + + + + + Folders + + + + + Content + + + + + File contains: + + + + + Case insensiti&ve + + + + + &Use regular expression + + + + + Properties + Eigenschaften + + + + File Size: + + + + + Larger than: + + + + + + Bytes + + + + + + KiB + + + + + + MiB + + + + + + GiB + + + + + Smaller than: + + + + + Last Modified Time: + + + + + Earlier than: + + + + + Later than: + + + + diff --git a/src/translations/libfm-qt_el.ts b/src/translations/libfm-qt_el.ts new file mode 100644 index 0000000..4b2d511 --- /dev/null +++ b/src/translations/libfm-qt_el.ts @@ -0,0 +1,1285 @@ + + + + + AppChooserDialog + + + Choose an Application + Επιλέξτε μια εφαρμογή + + + + Installed Applications + Εγκατεστημένες εφαρμογές + + + + Custom Command + Προσαρμοσμένη εντολή + + + + Command line to execute: + Γραμμή εντολών προς εκτέλεση: + + + + Application name: + Όνομα της εφαρμογής: + + + + <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> + <b>Αυτοί οι ειδικοί κωδικοί μπορούν να χρησιμοποιηθούν στη γραμμή εντολών:</b> +<ul> +<li><b>%f</b>: Αναπαριστά ένα όνομα αρχείου</li> +<li><b>%F</b>: Αναπαριστά πολλαπλά ονόματα αρχείων</li> +<li><b>%u</b>: Αναπαριστά ένα URI του αρχείου</li> +<li><b>%U</b>: Αναπαριστά πολλαπλά URI</li> +</ul> + + + + Keep terminal window open after command execution + Διατήρηση του παραθύρου του τερματικού ανοιχτό μετά την εκτέλεση της εντολής + + + + Execute in terminal emulator + Εκτέλεση στον προσομοιωτή τερματικού + + + + Set selected application as default action of this file type + Ορίστε την επιλεγμένη εφαρμογή ως την εξ ορισμού ενέργεια για αυτού του τύπου αρχεία + + + + EditBookmarksDialog + + + Edit Bookmarks + Επεξεργασία σελιδοδεικτών + + + + Name + Όνομα + + + + Location + Τοποθεσία + + + + &Add Item + &Προσθήκη αντικειμένου + + + + &Remove Item + &Αφαίρεση αντικειμένου + + + + Use drag and drop to reorder the items + Χρησιμοποιήστε τη μεταφορά και απόθεση για αναδιάταξη των αντικειμένων + + + + ExecFileDialog + + + Execute file + Εκτέλεση του αρχείου + + + + &Open + Ά&νοιγμα + + + + E&xecute + &Εκτέλεση + + + + Execute in &Terminal + Εκτέλεση στο &τερματικό + + + + Cancel + Ακύρωση + + + + FileOperationDialog + + + Destination: + Προορισμός: + + + + Processing: + Επεξεργασία: + + + + Preparing... + Προετοιμασία... + + + + Progress + Πρόοδος + + + + Time remaining: + Υπολειπόμενος χρόνος: + + + + FilePropsDialog + + + File Properties + Ιδιότητες του αρχείου + + + + General + Γενικά + + + + Location: + Τοποθεσία: + + + + File type: + Τύπος αρχείου: + + + + Mime type: + Τύπος Mime: + + + + File size: + Μέγεθος αρχείου: + + + + On-disk size: + Μέγεθος στον δίσκο: + + + + Last modified: + Τελευταία τροποποίηση: + + + + Link target: + Προορισμός συνδέσμου: + + + + Open With: + Άνοιγμα με: + + + + Last accessed: + Τελευταία προσπέλαση: + + + + Permissions + Άδειες + + + + Ownership + Ιδιοκτησία + + + + + + Group: + Ομάδα: + + + + + + Owner: + Ιδιοκτήτης: + + + + Access Control + Έλεγχος πρόσβασης + + + + + Other: + Άλλο: + + + + Make the file executable + Ορισμός ως εκτελέσιμο + + + + + + Read + Ανάγνωση + + + + + + Write + Εγγραφή + + + + + + Execute + Εκτέλεση + + + + Sticky + Κολλημένο + + + + SetUID + SetUID + + + + SetGID + SetGID + + + + Advanced Mode + Προηγμένη λειτουργία + + + + Fm::AppChooserComboBox + + + Customize + Προσαρμοσμένο + + + + Fm::AppChooserDialog + + + Select an application to open "%1" files + Επιλέξτε μια εφαρμογή για το άνοιγμα των αρχείων «%1» + + + + Fm::CreateNewMenu + + + Folder + Φάκελος + + + + Blank File + Κενό αρχείο + + + + Fm::DirTreeModel + + + Loading... + Φόρτωση... + + + + <No sub folders> + <No sub folders> + + + + Fm::DirTreeView + + + Open in New T&ab + + + + + Open in New Win&dow + + + + + Open in Termina&l + + + + + Fm::DndActionMenu + + + Copy here + Αντιγραφή εδώ + + + + Move here + Μετακίνηση εδώ + + + + Create symlink here + Δημιουργία συμβολικού δεσμού εδώ + + + + Cancel + Ακύρωση + + + + Fm::EditBookmarksDialog + + + New bookmark + Νέος σελιδοδείκτης + + + + Fm::ExecFileDialog + + + This text file '%1' seems to be an executable script. +What do you want to do with it? + Το αρχείο κειμένου «%1» φαίνεται ότι είναι ένα εκτελέσιμο σενάριο. +Τι θέλετε να κάνετε με αυτό; + + + + This file '%1' is executable. Do you want to execute it? + Αυτό το αρχείο «%1» είναι εκτελέσιμο. Θέλετε να το εκτελέσετε; + + + + Fm::FileMenu + + + Open + Άνοιγμα + + + + Open With... + Άνοιγμα με... + + + + Other Applications + Άλλες εφαρμογές + + + + Create &New + Δημιουργία &νέου + + + + &Restore + &Επαναφορά + + + + Cut + Αποκοπή + + + + Copy + Αντιγραφή + + + + Paste + Επικόλληση + + + + + &Move to Trash + &Μετακίνηση στα απορρίμματα + + + + Rename + Μετονομασία + + + + Extract to... + Εξαγωγή σε... + + + + Extract Here + Εξαγωγή εδώ + + + + Compress + Συμπίεση + + + + Properties + Ιδιότητες + + + + Output + Έξοδος + + + + &Delete + &Διαγραφή + + + + Fm::FileOperation + + + Error + Σφάλμα + + + + Some files cannot be moved to trash can because the underlying file systems don't support this operation. +Do you want to delete them instead? + Μερικά αρχεία δεν μπορούν να μετακινηθούν στον κάδο απορριμμάτων διότι το υποκείμενο αρχείο συστήματος δεν υποστηρίζει αυτήν την ενέργεια. +Θέλετε αντί αυτού να το διαγράψετε; + + + + + Confirm + Επιβεβαίωση + + + + Do you want to delete the selected files? + Επιθυμείτε την διαγραφή των επιλεγμένων αρχείων; + + + + Do you want to move the selected files to trash can? + Επιθυμείτε την μετακίνηση των επιλεγμένων αρχείων στον κάδο απορριμμάτων; + + + + Fm::FileOperationDialog + + + Move files + Μετακίνηση των αρχείων + + + + Moving the following files to destination folder: + Μετακίνηση των ακολούθων αρχείων στον φάκελο προορισμού: + + + + Copy Files + Αντιγραφή των αρχείων + + + + Copying the following files to destination folder: + Αντιγραφή των ακολούθων αρχείων στον φάκελο προορισμού: + + + + Trash Files + Ρίψη των αρχείων στα απορρίμματα + + + + Moving the following files to trash can: + Μετακίνηση των ακολούθων αρχείων στον κάδο απορριμμάτων: + + + + Delete Files + Διαγραφή των αρχείων + + + + Deleting the following files + Διαγραφή των ακολούθων αρχείων + + + + Create Symlinks + Δημιουργία συμβολικών δεσμών + + + + Creating symlinks for the following files: + Δημιουργία συμβολικών δεσμών για τα ακόλουθα αρχεία: + + + + Change Attributes + Αλλαγή ιδιοχαρακτηριστικών + + + + Changing attributes of the following files: + Αλλαγή των ιδιοχαρακτηριστικών των ακολούθων αρχείων: + + + + Restore Trashed Files + Επαναφορά των αρχείων από τον κάδο απορριμμάτων + + + + Restoring the following files from trash can: + Επαναφέρονται τα παρακάτω αρχεία από τον κάδο απορριμμάτων: + + + + Error + Σφάλμα + + + + Fm::FilePropsDialog + + + View folder content + Προβολή των περιεχομένων του φακέλου + + + + View and modify folder content + Προβολή και τροποποίηση των περιεχομένων του φακέλου + + + + Read + Ανάγνωση + + + + Read and write + Ανάγνωση και εγγραφή + + + + Forbidden + Απαγορευμένο + + + + Files of different types + Αρχεία διαφορετικού τύπου + + + + Multiple Files + Πολλαπλά αρχεία + + + + Apply changes + Εφαρμογή των αλλαγών + + + + Do you want to recursively apply these changes to all files and sub-folders? + Θέλετε να εφαρμόσετε αναδρομικά αυτές τις αλλαγές σε όλα τα αρχεία και υποφακέλους; + + + + Fm::FileSearchDialog + + + Error + Σφάλμα + + + + You should add at least add one directory to search. + + + + + Select a folder + + + + + Fm::FolderMenu + + + Create &New + Δημιουργία &νέου + + + + &Paste + Επι&κόλληση + + + + Select &All + Επιλογή ό&λων + + + + Invert Selection + Αντιστροφή επιλογής + + + + Sorting + Ταξινόμηση + + + + Show Hidden + Εμφάνιση των κρυφών + + + + Folder Pr&operties + Ι&διότητες του φακέλου + + + Folder + Φάκελος + + + Blank File + Κενό αρχείο + + + + By File Name + Ανά όνομα αρχείου + + + + By Modification Time + Ανά χρόνο τροποποίησης + + + + By File Size + Ανά μέγεθος αρχείου + + + + By File Type + Ανά τύπο αρχείου + + + + By File Owner + Ανά ιδιοκτήτη αρχείου + + + + Ascending + Αύξουσα + + + + Descending + Φθίνουσα + + + + Folder First + Οι φάκελοι πρώτα + + + + Case Sensitive + Διάκριση πεζών/κεφαλαίων + + + + Fm::FolderModel + + + Name + Όνομα + + + + Type + Τύπος + + + + Size + Μέγεθος + + + + Modified + Τροποποιήθηκε + + + + Owner + Ιδιοκτήτης + + + + Fm::FontButton + + + Bold + Έντονα + + + + Italic + Πλάγια + + + + Fm::MountOperationPasswordDialog + + + &Connect + &Σύνδεση + + + + Fm::PlacesModel + + + Places + Τοποθεσίες + + + + Desktop + Επιφάνεια εργασίας + + + + Computer + Υπολογιστής + + + + Applications + Εφαρμογές + + + + Network + Δίκτυο + + + + Devices + Συσκευές + + + + Bookmarks + Σελιδοδείκτες + + + + Trash + Απορρίμματα + + + + Fm::PlacesView + + + Open in New Tab + Άνοιγμα σε νέα καρτέλα + + + + Open in New Window + Άνοιγμα σε νέο παράθυρο + + + + Empty Trash + Άδειασμα των απορριμμάτων + + + + Move Bookmark Up + Μετακίνηση του σελιδοδείκτη προς τα πάνω + + + + Move Bookmark Down + Μετακίνηση του σελιδοδείκτη προς τα κάτω + + + + Rename Bookmark + Μετονομασία σελιδοδείκτη + + + + Remove Bookmark + Αφαίρεση σελιδοδείκτη + + + + + Unmount + Αποπροσάρτηση + + + + Mount + Προσάρτηση + + + + Eject + Εξαγωγή + + + + Fm::RenameDialog + + + + Type: %1 +Size: %2 +Modified: %3 + Τύπος: %1 +Μέγεθος: %2 +Τροποποιήθηκε: %3 + + + + Type: %1 +Modified: %2 + Τύπος: %1 +Τροποποιήθηκε: %2 + + + + Type: %1 +Modified: %3 + Τύπος: %1 +Τροποποιήθηκε: %3 + + + + &Overwrite + &Αντικατάσταση + + + + &Rename + &Μετονομασία + + + + Fm::SidePane + + + + Places + Τοποθεσίες + + + + + Directory Tree + Δέντρο καταλόγων + + + + Shows list of common places, devices, and bookmarks in sidebar + Εμφανίζει τη λίστα με τις κοινές τοποθεσίες, συσκευές, και σελιδοδείκτες στην πλευρική στήλη + + + + Shows tree of directories in sidebar + Εμφανίζει ένα δέντρο των καταλόγων στην πλευρική στήλη + + + + MountOperationPasswordDialog + + + Mount + Προσάρτηση + + + + Connect &anonymously + &Ανώνυμη σύνδεση + + + + Connect as u&ser: + Σύνδεση ως &χρήστης: + + + + &Username: + Όνομα χ&ρήστη: + + + + &Password: + &Κωδικός πρόσβασης: + + + + &Domain: + &Τομέας: + + + + Forget password &immediately + &Λήθη του κωδικού πρόσβασης άμεσα + + + + Remember password until you &logout + Απομνημόνευση του κωδικού πρόσβασης &μέχρι να αποσυνδεθείτε + + + + Remember &forever + Απομνημόνευση ε&σαεί + + + + QObject + + + Rename File + Μετονομασία αρχείου + + + + Please enter a new name: + Παρακαλώ εισαγάγετε ένα νέο όνομα: + + + + + + + Error + Σφάλμα + + + + Create Folder + Δημιουργία φακέλου + + + + Create File + Δημιουργία αρχείου + + + + Please enter a new file name: + Παρακαλώ εισαγάγετε ένα νέο όνομα αρχείου: + + + + New text file + Νέο αρχείο κειμένου + + + + Please enter a new folder name: + Παρακαλώ εισαγάγετε ένα νέο όνομα φακέλου: + + + + New folder + Νέος φάκελος + + + + Enter a name for the new %1: + Εισαγάγετε ένα όνομα για το νέο %1: + + + + RenameDialog + + + Confirm to replace files + Επιβεβαίωση αντικατάστασης των αρχείων + + + + <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> + <html><head/><body><p><span style=" font-weight:600;">Υπάρχει ήδη ένα αρχείο με το ίδιο όνομα στην συγκεκριμένη τοποθεσία.</span></p><p>Θέλετε να αντικαταστήσετε το υπάρχον αρχείο;</p></body></html> + + + + dest + προορισμός + + + + with the following file? + με το παρακάτω αρχείο; + + + + src file info + πληροφορίες αρχείου πηγής + + + + dest file info + πληροφορίες αρχείου προορισμού + + + + src + πηγή + + + + &File name: + &Όνομα αρχείου: + + + + Apply this option to all existing files + Εφαρμογή της επιλογής σε όλα τα υπάρχοντα αρχεία + + + + SearchDialog + + + Search Files + + + + + Name/Location + + + + + File Name Patterns: + + + + + * + + + + + Case insensitive + + + + + Use regular expression + + + + + Places to Search: + + + + + &Add + + + + + &Remove + + + + + Search in sub directories + + + + + Search for hidden files + + + + + File Type + + + + + Only search for files of following types: + + + + + Text files + + + + + Image files + + + + + Audio files + + + + + Video files + + + + + Documents + + + + + Folders + + + + + Content + + + + + File contains: + + + + + Case insensiti&ve + + + + + &Use regular expression + + + + + Properties + Ιδιότητες + + + + File Size: + + + + + Larger than: + + + + + + Bytes + + + + + + KiB + + + + + + MiB + + + + + + GiB + + + + + Smaller than: + + + + + Last Modified Time: + + + + + Earlier than: + + + + + Later than: + + + + diff --git a/src/translations/libfm-qt_es.ts b/src/translations/libfm-qt_es.ts new file mode 100644 index 0000000..090f33c --- /dev/null +++ b/src/translations/libfm-qt_es.ts @@ -0,0 +1,1293 @@ + + + + + AppChooserDialog + + + Choose an Application + Seleccione una Aplicación + + + + Installed Applications + Aplicaciones Instaladas + + + + Custom Command + Comando Personalizado + + + + Command line to execute: + Línea de comandos a ejecutar: + + + + Application name: + Nombre de la aplicación: + + + + <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> + <b>Estos códigos pueden ser usados en la línea de comando:</b> +<ul> +<li><b>%f</b>: Representa un nombre de archivo</li> +<li><b>%F</b>: Representa múltiples nombres de archivos</li> +<li><b>%u</b>: Representa una URI para el archivo</li> +<li><b>%U</b>: Representa múltiples URIs</li> +</ul> + + + + Keep terminal window open after command execution + Mantener la terminal abierta luego de la ejecución del comando + + + + Execute in terminal emulator + Ejecutar en un emulador de terminal + + + + Set selected application as default action of this file type + Configurar la aplicación seleccionada como por defecto para este tipo de archivo + + + + EditBookmarksDialog + + + Edit Bookmarks + Editar marcadores + + + + Name + Nombre + + + + Location + Localización + + + + &Add Item + &Agregar elemento + + + + &Remove Item + &Eliminar elemento + + + + Use drag and drop to reorder the items + Arrastre y suelte para reordenar los elementos + + + + ExecFileDialog + + + Execute file + Ejecutar archivo + + + + &Open + &Abrir + + + + E&xecute + &Ejecutar + + + + Execute in &Terminal + Ejecutar en &Terminal + + + + Cancel + Cancelar + + + + FileOperationDialog + + + Destination: + Destino: + + + + Processing: + Procesando: + + + + Preparing... + Preparando... + + + + Progress + Progreso + + + + Time remaining: + Tiempo restante: + + + + FilePropsDialog + + + File Properties + Propiedades de Archivo + + + + General + General + + + + Location: + Localización: + + + + File type: + Tipo de archivo: + + + + Mime type: + Tipo Mime: + + + + File size: + Tamaño del archivo: + + + + On-disk size: + Tamaño en disco: + + + + Last modified: + Última modificación: + + + + Link target: + Destino del enlace: + + + + Open With: + Abrir con: + + + + Last accessed: + Último acceso: + + + + Permissions + Permisos + + + + Ownership + Propiedad + + + + + + Group: + Grupo: + + + + + + Owner: + Dueño: + + + + Access Control + Control de acceso + + + + + Other: + Otro: + + + + Make the file executable + Marcar como ejecutable + + + + + + Read + Lectura + + + + + + Write + Escritura + + + + + + Execute + Ejecución + + + + Sticky + Sticky + + + + SetUID + SetUID + + + + SetGID + SetGID + + + + Advanced Mode + Modo avanzado + + + + Fm::AppChooserComboBox + + + Customize + Personalizar + + + + Fm::AppChooserDialog + + + Select an application to open "%1" files + Seleccione una aplicación para abrir archivos "%1" + + + + Fm::CreateNewMenu + + + Folder + Directorio + + + + Blank File + Archivo Vacío + + + + Fm::DirTreeModel + + + Loading... + Cargando... + + + + <No sub folders> + <No hay subdirectorios> + + + + Fm::DirTreeView + + + Open in New T&ab + + + + + Open in New Win&dow + + + + + Open in Termina&l + + + + + Fm::DndActionMenu + + + Copy here + Copiar aquí + + + + Move here + Mover aquí + + + + Create symlink here + Crear enlace simbólico aquí + + + + Cancel + Cancelar + + + + Fm::EditBookmarksDialog + + + New bookmark + Nuevo marcador + + + + Fm::ExecFileDialog + + + This text file '%1' seems to be an executable script. +What do you want to do with it? + El archivo de texto '%1' parece ser un script ejecutable. +¿Qué desea hacer con él? + + + + This file '%1' is executable. Do you want to execute it? + El archivo '%1' es ejecutable. ¿Quiere ejecutarlo? + + + + Fm::FileMenu + + + Open + Abrir + + + + Open With... + Abrir con... + + + + Other Applications + Otras Aplicaciones + + + + Create &New + Crear &Nuevo + + + + &Restore + &Restaurar + + + + Cut + Cortar + + + + Copy + Copiar + + + + Paste + Pegar + + + + + &Move to Trash + &Mover a la Papelera + + + + Rename + Renombrar + + + + Extract to... + Extraer en... + + + + Extract Here + Extraer aquí + + + + Compress + Comprimir + + + + Properties + Propiedades + + + + Output + Salida + + + + &Delete + &Borrar + + + + Fm::FileOperation + + + Error + Error + + + + Some files cannot be moved to trash can because the underlying file systems don't support this operation. +Do you want to delete them instead? + Algunos archivos no pueden moverse a la papelera porque el subsistema de archivos no permite esta operación. +¿Quiere eliminarlos en su lugar? + + + + + Confirm + Confirmación + + + + Do you want to delete the selected files? + ¿Quiere borrar los archivos seleccionados? + + + + Do you want to move the selected files to trash can? + ¿Quiere mover los archivos seleccionados a la papelera? + + + + Fm::FileOperationDialog + + + Move files + Mover archivos + + + + Moving the following files to destination folder: + Moviendo los siguientes archivos al directorio destino: + + + + Copy Files + Copiar archivos + + + + Copying the following files to destination folder: + Copiando los siguientes archivos al directorio destino: + + + + Trash Files + Enviar archivos a la papelera + + + + Moving the following files to trash can: + Moviendo los siguientes archivos a la papelera: + + + + Delete Files + Borrar archivos + + + + Deleting the following files + Borrando los siguientes archivos + + + + Create Symlinks + Crear enlaces simbólicos + + + + Creating symlinks for the following files: + Creando enlaces simbólicos para los siguientes archivos: + + + + Change Attributes + Cambiar atributos + + + + Changing attributes of the following files: + Cambiando atributos para los siguientes archivos: + + + + Restore Trashed Files + Restaurar archivos borrados + + + + Restoring the following files from trash can: + Restaurando los siguientes archivos desde la papelera: + + + + Error + Error + + + + Fm::FilePropsDialog + + + View folder content + Ver contenido del directorio + + + + View and modify folder content + Ver y modificar el contenido del directorio + + + + Read + Lectura + + + + Read and write + Lectura y escritura + + + + Forbidden + Prohibido + + + + Files of different types + Archivos de diferentes tipos + + + + Multiple Files + Múltiples archivos + + + + Apply changes + Aplicar cambios + + + + Do you want to recursively apply these changes to all files and sub-folders? + ¿Quiere aplicar los cambios a todos los archivos y subdirectorios? + + + + Fm::FileSearchDialog + + + Error + Error + + + + You should add at least add one directory to search. + + + + + Select a folder + + + + + Fm::FolderMenu + + + Create &New + Crear &Nuevo + + + + &Paste + &Pegar + + + + Select &All + Seleccion&ar todo + + + + Invert Selection + Invertir Selección + + + + Sorting + Ordenar + + + + Show Hidden + Mostrar Ocultos + + + + Folder Pr&operties + Pr&opiedades del directorio + + + Folder + Directorio + + + Blank File + Archivo Vacío + + + + By File Name + Por nombre de archivo + + + + By Modification Time + Por fecha de modificación + + + + By File Size + Por tamaño de archivo + + + + By File Type + Por tipo de archivo + + + + By File Owner + Por dueño del archivo + + + + Ascending + Ascendente + + + + Descending + Descendente + + + + Folder First + Primero Directorios + + + + Case Sensitive + Coincidir mayúsculas + + + + Fm::FolderModel + + + Name + Nombre + + + + Type + Tipo + + + + Size + Tamaño + + + + Modified + Modificado + + + + Owner + Dueño + + + + Fm::FontButton + + + Bold + Negrita + + + + Italic + Itálica + + + + Fm::MountOperationPasswordDialog + + + &Connect + &Conectar + + + + Fm::PlacesModel + + + Places + Lugares + + + + Desktop + Escritorio + + + + Computer + Sistema + + + + Applications + Aplicaciones + + + + Network + Red + + + + Devices + Dispositivos + + + + Bookmarks + Marcadores + + + + Trash + Papelera + + + + Fm::PlacesView + + + Empty Trash + Vaciar Papelera + + + Rename + Renombrar + + + Delete + Borrar + + + + Open in New Tab + + + + + Open in New Window + + + + + Move Bookmark Up + + + + + Move Bookmark Down + + + + + Rename Bookmark + + + + + Remove Bookmark + + + + + + Unmount + Desmontar + + + + Mount + Montar + + + + Eject + Expulsar + + + + Fm::RenameDialog + + + + Type: %1 +Size: %2 +Modified: %3 + Tipo: %1 +Tamaño: %2 +Modificado: %3 + + + + Type: %1 +Modified: %2 + Tipo: %1 +Modificado: %2 + + + + Type: %1 +Modified: %3 + Tipo: %1 +Modificado: %3 + + + + &Overwrite + &Sobreescribir + + + + &Rename + &Renombrar + + + + Fm::SidePane + + + + Places + Lugares + + + + + Directory Tree + Árbol de Directorios + + + + Shows list of common places, devices, and bookmarks in sidebar + Muestra lista de lugares comunes, dispositivos y marcadores en la barra lateral + + + + Shows tree of directories in sidebar + Muestra árbol de directorios en barra lateral + + + + MountOperationPasswordDialog + + + Mount + Montar + + + + Connect &anonymously + Conectar &anonimamente + + + + Connect as u&ser: + Conectar como u&suario: + + + + &Username: + &Usuario: + + + + &Password: + &Contraseña: + + + + &Domain: + &Dominio: + + + + Forget password &immediately + Descartar contraseña &inmediatamente + + + + Remember password until you &logout + &Recordar contraseña hasta cerrar sesión + + + + Remember &forever + Recordar &para siempre + + + + QObject + + + + + + Error + Error + + + + Rename File + Renombrar Archivo + + + + Please enter a new name: + Introduzca un nuevo nombre: + + + + Create Folder + + + + + Please enter a new file name: + Introduzca un nuevo nombre de archivo: + + + + New text file + Nuevo archivo de texto + + + + Please enter a new folder name: + Ingrese un nuevo nombre de directorio: + + + + New folder + Nuevo directorio + + + + Enter a name for the new %1: + Ingresar un nombre para el nuevo %1: + + + + Create File + Crear Archivo + + + + RenameDialog + + + Confirm to replace files + Confirmación de sustitución + + + + <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> + <html><head/><body><p><span style=" font-weight:600;">Ya existe un archivo con el mismo nombre en este lugar.</span></p><p>¿Quiere reemplazar el archivo existente?</p></body></html> + + + + dest + destino + + + + with the following file? + con el siguiente archivo? + + + + src file info + información del archivo de origen + + + + dest file info + información del archivo de destino + + + + src + origen + + + + &File name: + Nombre de &archivo: + + + + Apply this option to all existing files + Aplicar esta opción a todos los archivos + + + + SearchDialog + + + Search Files + + + + + Name/Location + + + + + File Name Patterns: + + + + + * + + + + + Case insensitive + + + + + Use regular expression + + + + + Places to Search: + + + + + &Add + + + + + &Remove + + + + + Search in sub directories + + + + + Search for hidden files + + + + + File Type + + + + + Only search for files of following types: + + + + + Text files + + + + + Image files + + + + + Audio files + + + + + Video files + + + + + Documents + + + + + Folders + + + + + Content + + + + + File contains: + + + + + Case insensiti&ve + + + + + &Use regular expression + + + + + Properties + Propiedades + + + + File Size: + + + + + Larger than: + + + + + + Bytes + + + + + + KiB + + + + + + MiB + + + + + + GiB + + + + + Smaller than: + + + + + Last Modified Time: + + + + + Earlier than: + + + + + Later than: + + + + diff --git a/src/translations/libfm-qt_fr.ts b/src/translations/libfm-qt_fr.ts new file mode 100644 index 0000000..ee00c16 --- /dev/null +++ b/src/translations/libfm-qt_fr.ts @@ -0,0 +1,1296 @@ + + + + + AppChooserDialog + + + Choose an Application + Sélectionnez une application + + + + Installed Applications + Applications installées + + + + Custom Command + Commandes personnalisées + + + + Command line to execute: + Ligne de commande a exécuter : + + + + Application name: + Nom de l’application : + + + + <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> + <b>Ces codes spéciaux peuvent être utilisées dans la ligne de commande :</b> +<ul> +<li><b>%f</b>: Represente un seul nom de fichier</li> +<li><b>%F</b>: Represente plusieurs nom de fichiers</li> +<li><b>%u</b>: Represente l’URI d'un seul fichier</li> +<li><b>%U</b>: Represente plusieurs URIs</li> +</ul> + + + + Keep terminal window open after command execution + Garder la fenêtre de terminal ouverte après exécution de la commande + + + + Execute in terminal emulator + Exécuter dans un émulateur de terminal + + + + Set selected application as default action of this file type + Définir l’application sélectionnée par défaut pour les fichiers de ce type + + + + EditBookmarksDialog + + + Edit Bookmarks + Modifier les signets + + + + Name + Nom + + + + Location + Emplacement + + + + &Add Item + &Ajouter un élément + + + + &Remove Item + &Supprimer un élément + + + + Use drag and drop to reorder the items + Utiliser le glisser-déposer pour trier à nouveau les éléments + + + + ExecFileDialog + + + Execute file + Exécuter le fichier + + + + &Open + &Ouvrir + + + + E&xecute + E&xécuter + + + + Execute in &Terminal + Exécuter dans un &terminal + + + + Cancel + Annuler + + + + FileOperationDialog + + + Destination: + Destination : + + + + Processing: + Traitement : + + + + Preparing... + Préparation… + + + + Progress + Progression + + + + Time remaining: + Temps restant : + + + + FilePropsDialog + + + File Properties + Propriétés du fichier + + + + General + Général + + + + Location: + Emplacement : + + + + File type: + Type de fichier : + + + + Mime type: + Type Mime : + + + + File size: + Taille du fichier : + + + + On-disk size: + Taille sur le disque : + + + + Last modified: + Date de dernière modification : + + + + Link target: + Cible du lien : + + + + Open With: + Ouvrir avec : + + + + Last accessed: + Date de dernier accès : + + + + Permissions + Droits d'accès + + + + Ownership + Propriété + + + + + + Group: + Groupe : + + + + + + Owner: + Propriétaire : + + + + Access Control + Contrôle d’accès + + + + + Other: + Autre : + + + + Make the file executable + Rendre le fichier exécutable + + + + + + Read + Lecture + + + + + + Write + Écriture + + + + + + Execute + Exécution + + + + Sticky + Permanent + + + + SetUID + SetUID + + + + SetGID + SetGID + + + + Advanced Mode + Mode avancé + + + + Fm::AppChooserComboBox + + + Customize + Personnaliser + + + + Fm::AppChooserDialog + + + Select an application to open "%1" files + Sélectionnez une application pour ouvrir les fichiers de type "%1" + + + + Fm::CreateNewMenu + + + Folder + Dossier + + + + Blank File + Fichier vide + + + + Fm::DirTreeModel + + + Loading... + Chargement… + + + + <No sub folders> + <Aucun sous-dossiers> + + + + Fm::DirTreeView + + + Open in New T&ab + Ouvrir dans un nouvel &onglet + + + + Open in New Win&dow + Ouvrir dans une nouvelle &fenêtre + + + + Open in Termina&l + Ouvrir dans un &terminal + + + + Fm::DndActionMenu + + + Copy here + Copier ici + + + + Move here + Déplacer ici + + + + Create symlink here + Créer un lien symbolique ici + + + + Cancel + Annuler + + + + Fm::EditBookmarksDialog + + + New bookmark + Nouveau signet + + + + Fm::ExecFileDialog + + + This text file '%1' seems to be an executable script. +What do you want to do with it? + + + + + This file '%1' is executable. Do you want to execute it? + Le fichier '%1' est exécutable. Souhaitez-vous le lancer ? + + + + Fm::FileMenu + + + Open + Ouvrir + + + OpenWith + OpenWith + + + + Cut + Couper + + + + Copy + Copier + + + + Paste + Coller + + + + + &Move to Trash + &Mettre à la corbeille + + + + Output + + + + + &Delete + &Supprimer + + + + Rename + Renommer + + + + Open With... + Ouvrir avec… + + + + Other Applications + Autres applications + + + + Create &New + Créer un &nouveau + + + + &Restore + Restaurer + + + + Extract to... + Extraire vers… + + + + Extract Here + Extraire ici + + + + Compress + Compresser + + + + Properties + Propriétés + + + + Fm::FileOperation + + + Error + Erreur + + + + Some files cannot be moved to trash can because the underlying file systems don't support this operation. +Do you want to delete them instead? + Certains fichiers ne peuvent pas être mis à la corbeille car les systèmes de fichiers sous-jacents ne prennent pas en charge cette opération. +Voulez-vous plutôt les supprimer ? + + + + + Confirm + Confirmer + + + + Do you want to delete the selected files? + Voulez-vous supprimer les fichiers sélectionnés ? + + + + Do you want to move the selected files to trash can? + Voulez-vous mettre les fichiers sélectionnés à la corbeille ? + + + + Fm::FileOperationDialog + + + Move files + Déplacer les fichiers + + + + Moving the following files to destination folder: + Déplacement des fichiers suivants vers le dossier de destination en cours : + + + + Copy Files + Copier les fichiers + + + + Copying the following files to destination folder: + Copie des fichiers suivants vers le dossier de destination en cours : + + + + Trash Files + Mettre les fichiers de la corbeille + + + + Moving the following files to trash can: + Déplacement des fichiers suivants vers la corbeille en cours : + + + + Delete Files + Supprimer les fichiers + + + + Deleting the following files + Suppression des fichiers suivants + + + + Create Symlinks + Créer des liens symboliques + + + + Creating symlinks for the following files: + Création de liens symboliques avec les fichiers suivants en cours : + + + + Change Attributes + Modifier les attributs + + + + Changing attributes of the following files: + Modification des attributs des fichiers suivants en cours : + + + + Restore Trashed Files + Restaurer les fichiers de la corbeille + + + + Restoring the following files from trash can: + Restauration des fichiers suivants depuis la corbeille en cours : + + + + Error + Erreur + + + + Fm::FilePropsDialog + + + View folder content + Voir le contenu du dossier + + + + View and modify folder content + Voir et modifier le contenu du dossier + + + + Read + Lecture + + + + Read and write + Lecture et écriture + + + + Forbidden + Interdit + + + + Files of different types + Fichiers de différents types + + + + Multiple Files + Fichiers multiples + + + + Apply changes + Appliquer les modifications + + + + Do you want to recursively apply these changes to all files and sub-folders? + Voulez-vous appliquer ces changements récursivement à tous les fichiers et sous-dossiers ? + + + + Fm::FileSearchDialog + + + Error + Erreur + + + + You should add at least add one directory to search. + Vous devriez indiquer au moins un dossier pour la recherche. + + + + Select a folder + Sélectionnez un dossier + + + + Fm::FolderMenu + + + Create &New + Créer un &nouveau + + + + &Paste + &Coller + + + + Select &All + Sélectionner &tous + + + + Invert Selection + Inverser la sélection + + + + Sorting + Tri en cours + + + + Show Hidden + Afficher les éléments cachés + + + + Folder Pr&operties + Pr&opriétés du dossier + + + Folder + Dossier + + + File + Fichier + + + + By File Name + Par nom de fichier + + + + By Modification Time + Par date de modification + + + + By File Size + Par taille de fichier + + + + By File Type + Par type de fichier + + + + By File Owner + Par propriétaire de fichier + + + + Ascending + Ascendant + + + + Descending + Descendant + + + + Folder First + Dossier en premier + + + + Case Sensitive + Sensible à la casse + + + + Fm::FolderModel + + + Name + Nom + + + + Type + Type + + + + Size + Taille + + + + Modified + Modifié + + + + Owner + Propriétaire + + + + Fm::FontButton + + + Bold + Gras + + + + Italic + Italique + + + + Fm::MountOperationPasswordDialog + + + &Connect + &Connecter + + + + Fm::PlacesModel + + + Places + Emplacements + + + + Desktop + Bureau + + + + Trash + Corbeille + + + + Computer + Ordinateur + + + + Applications + Applications + + + + Network + Réseau + + + + Devices + Appareils + + + + Bookmarks + Signets + + + + Fm::PlacesView + + + Empty Trash + Corbeille vide + + + Rename + Renommer + + + Delete + Supprimer + + + + Open in New Tab + Ouvrir dans un nouvel onglet + + + + Open in New Window + Ouvrir dans une nouvelle fenêtre + + + + Move Bookmark Up + Monter le signet + + + + Move Bookmark Down + Descendre le signet + + + + Rename Bookmark + Renommer le signet + + + + Remove Bookmark + Effacer le signet + + + + + Unmount + Démonter + + + + Mount + Monter + + + + Eject + Éjecter + + + + Fm::RenameDialog + + + + Type: %1 +Size: %2 +Modified: %3 + Type : %1 +Taille : %2 +Modification : %3 + + + + Type: %1 +Modified: %2 + Type : %1 +Modification : %2 + + + + Type: %1 +Modified: %3 + Type : %1 +Modification : %3 + + + + &Overwrite + &Remplacer + + + + &Rename + &Renommer + + + + Fm::SidePane + + + + Places + Emplacements + + + + + Directory Tree + + + + + Shows list of common places, devices, and bookmarks in sidebar + + + + + Shows tree of directories in sidebar + + + + + MountOperationPasswordDialog + + + Mount + Monter + + + + Connect &anonymously + Connecter &anonymement + + + + Connect as u&ser: + Connecter comme utilis&sateur : + + + + &Username: + Nom d'&utilisateur : + + + + &Password: + Mot de &passe : + + + + &Domain: + &Domaine : + + + + Forget password &immediately + Oublier le mot de passe &immédiatement + + + + Remember password until you &logout + Se souvenir du mot de passe jusqu'à &la déconnexion + + + + Remember &forever + &Toujours s'en souvenir + + + + QObject + + + + + + Error + Erreur + + + + Rename File + Renommer le fichier + + + + Please enter a new name: + Veuillez entrer un nouveau nom : + + + + Create Folder + Créer un dossier + + + + Please enter a new file name: + Veuillez entrer un nouveau nom de fichier : + + + + New text file + Nouveau fichier texte + + + + Please enter a new folder name: + Veuillez entrer un nouveau nom de répertoire : + + + + New folder + Nouveau répertoire + + + + Enter a name for the new %1: + Entrez un nom pour le nouveau %1 : + + + + Create File + Créer un fichier + + + + RenameDialog + + + Confirm to replace files + Confirmer le remplacement des fichiers + + + + <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> + <html><head/><body><p><span style=" font-weight:600;">Il existe déjà un fichier avec le même nom à cet emplacement.</span></p><p>Voulez-vous replacer le fichier existant ?</p></body></html> + + + + dest + dst + + + + with the following file? + avec le fichier suivant ? + + + + src file info + infos fichier src + + + + dest file info + infos fichier dst + + + + src + src + + + + &File name: + Nom de &fichier : + + + + Apply this option to all existing files + Appliquer cette option à tous les fichiers existants + + + + SearchDialog + + + Search Files + + + + + Name/Location + + + + + File Name Patterns: + + + + + * + * + + + + Case insensitive + Insensible à la casse + + + + Use regular expression + + + + + Places to Search: + + + + + &Add + &Ajout + + + + &Remove + + + + + Search in sub directories + + + + + Search for hidden files + + + + + File Type + Type de fichier + + + + Only search for files of following types: + + + + + Text files + + + + + Image files + + + + + Audio files + Fichiers audio + + + + Video files + + + + + Documents + Documents + + + + Folders + Dossiers + + + + Content + Contenu + + + + File contains: + Le fichier contient : + + + + Case insensiti&ve + &Insensible à la casse + + + + &Use regular expression + + + + + Properties + Propriétés + + + + File Size: + Taille du fichier : + + + + Larger than: + + + + + + Bytes + Octets + + + + + KiB + KiB + + + + + MiB + MiB + + + + + GiB + GiB + + + + Smaller than: + + + + + Last Modified Time: + + + + + Earlier than: + Plus récent que : + + + + Later than: + + + + diff --git a/src/translations/libfm-qt_gl.ts b/src/translations/libfm-qt_gl.ts new file mode 100644 index 0000000..43e5874 --- /dev/null +++ b/src/translations/libfm-qt_gl.ts @@ -0,0 +1,1290 @@ + + + + + AppChooserDialog + + + Choose an Application + + + + + Installed Applications + + + + + Custom Command + + + + + Command line to execute: + + + + + Application name: + + + + + <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> + + + + + Keep terminal window open after command execution + + + + + Execute in terminal emulator + + + + + Set selected application as default action of this file type + + + + + EditBookmarksDialog + + + Edit Bookmarks + Editar marcadores + + + + Name + Nome + + + + Location + Localización + + + + &Add Item + &Engadir un elemento + + + + &Remove Item + &Retirar o elemento + + + + Use drag and drop to reorder the items + Usar arrastrar e soltar para ordenar elementos + + + + ExecFileDialog + + + Execute file + + + + + &Open + + + + + E&xecute + + + + + Execute in &Terminal + + + + + Cancel + Cancelar + + + + FileOperationDialog + + + Destination: + Destino: + + + + Processing: + Procesando: + + + + Preparing... + Preparando... + + + + Progress + Progreso + + + + Time remaining: + Tempo restante: + + + + FilePropsDialog + + + File Properties + Propiedades do ficheiro + + + + General + Xeral + + + + Location: + Localización + + + + File type: + Tipo de ficheiro: + + + + Mime type: + Tipo MIME: + + + + File size: + Tamaño do ficheiro: + + + + On-disk size: + Tamaño no disco: + + + + Last modified: + Última modificación: + + + + Link target: + Destino da ligazón: + + + + Open With: + Abrir con: + + + + Last accessed: + Último acceso: + + + + Permissions + Permisos + + + + Ownership + Dono + + + + + + Group: + Grupo: + + + + + + Owner: + Propietario: + + + + Access Control + Control de acceso + + + + + Other: + Outros: + + + + Make the file executable + Facer que o ficheiro sexa executábel + + + + + + Read + Lectura + + + + + + Write + Escritura + + + + + + Execute + Execución + + + + Sticky + Persistente + + + + SetUID + Estabelecer o UID + + + + SetGID + Estabelecer o GID + + + + Advanced Mode + Modo avanzado + + + + Fm::AppChooserComboBox + + + Customize + + + + + Fm::AppChooserDialog + + + Select an application to open "%1" files + + + + + Fm::CreateNewMenu + + + Folder + Cartafol + + + + Blank File + + + + + Fm::DirTreeModel + + + Loading... + + + + + <No sub folders> + + + + + Fm::DirTreeView + + + Open in New T&ab + + + + + Open in New Win&dow + + + + + Open in Termina&l + + + + + Fm::DndActionMenu + + + Copy here + Copiar para aquí + + + + Move here + Mover para aquí + + + + Create symlink here + Crear aquí unha ligazón simbólica + + + + Cancel + Cancelar + + + + Fm::EditBookmarksDialog + + + New bookmark + Novo marcador + + + + Fm::ExecFileDialog + + + This text file '%1' seems to be an executable script. +What do you want to do with it? + + + + + This file '%1' is executable. Do you want to execute it? + + + + + Fm::FileMenu + + + Open + Abrir + + + OpenWith + Abrir con + + + + Cut + Cortar + + + + Copy + Copiar + + + + Paste + Pegar + + + + + &Move to Trash + Deitar no lixo + + + + Output + + + + + &Delete + &Eliminar + + + + Rename + Renomear + + + + Open With... + + + + + Other Applications + + + + + Create &New + Crear &novo + + + + &Restore + + + + + Extract to... + Extraer en… + + + + Extract Here + Extraer aquí + + + + Compress + Comprimir + + + + Properties + Propiedades + + + + Fm::FileOperation + + + Error + Erro + + + + Some files cannot be moved to trash can because the underlying file systems don't support this operation. +Do you want to delete them instead? + Algúns ficheiros non poden enviarse ao cesto do lixo porque o subsistema de ficheiros non permite esta operación. +Desexa eliminalos no seu canto? + + + + + Confirm + Confirmar + + + + Do you want to delete the selected files? + Quere eliminar os ficheiros seleccionados? + + + + Do you want to move the selected files to trash can? + Quere mover os ficheiros seleccionados ao cesto do lixo? + + + + Fm::FileOperationDialog + + + Move files + Mover os ficheiros + + + + Moving the following files to destination folder: + Movendo os seguintes ficheiros ao cartafol de destino: + + + + Copy Files + Copiar os ficheiros + + + + Copying the following files to destination folder: + Copiando os seguintes ficheiros ao cartafol de destino: + + + + Trash Files + Deitar no lixo os ficheiros + + + + Moving the following files to trash can: + Movendo os seguintes ficheiros ao lixo: + + + + Delete Files + Eliminar os ficheiros + + + + Deleting the following files + Eliminando os seguintes ficheiros + + + + Create Symlinks + Crear ligazóns simbólicas + + + + Creating symlinks for the following files: + Creando ligazóns simbólicas para os seguintes ficheiros: + + + + Change Attributes + Cambiar os atributos + + + + Changing attributes of the following files: + Cambiando os atributos dos seguintes ficheiros: + + + + Restore Trashed Files + Restaurar os ficheiro do lixo + + + + Restoring the following files from trash can: + Restaurando os seguintes ficheiros do lixo: + + + + Error + Erro + + + + Fm::FilePropsDialog + + + View folder content + Ver o contido do cartafol + + + + View and modify folder content + Ver e modificar o contido do cartafol + + + + Read + Lectura + + + + Read and write + Lectura e escritura + + + + Forbidden + Prohibido + + + + Files of different types + Ficheiros de tipos diferentes + + + + Multiple Files + Múltiplos ficheiros + + + + Apply changes + Aplicar os cambios + + + + Do you want to recursively apply these changes to all files and sub-folders? + Quere aplicar recursivamente estes cambios a todos os ficheiros e subcartafoles? + + + + Fm::FileSearchDialog + + + Error + Erro + + + + You should add at least add one directory to search. + + + + + Select a folder + + + + + Fm::FolderMenu + + + Create &New + Crear &novo + + + + &Paste + &Pegar + + + + Select &All + Seleccionar &todo + + + + Invert Selection + Inverter a selección + + + + Sorting + Ordenación + + + + Show Hidden + Amosar agochados + + + + Folder Pr&operties + Pr&opiedades do cartafol + + + Folder + Cartafol + + + File + Ficheiro + + + + By File Name + Polo nome do ficheiro + + + + By Modification Time + Pola data de modificación + + + + By File Size + Polo tamaño do ficheiro + + + + By File Type + Polo tipo do ficheiro + + + + By File Owner + Polo propietario do ficheiro + + + + Ascending + Ascendente + + + + Descending + Descendente + + + + Folder First + Primeiro os cartafoles + + + + Case Sensitive + Distinguindo maiúsculas de minúsculas + + + + Fm::FolderModel + + + Name + Nome + + + + Type + Tipo + + + + Size + Tamaño + + + + Modified + Modificado + + + + Owner + Propietario + + + + Fm::FontButton + + + Bold + Negra + + + + Italic + Itálica + + + + Fm::MountOperationPasswordDialog + + + &Connect + &Conectar + + + + Fm::PlacesModel + + + Places + Lugares + + + + Desktop + Escritorio + + + + Trash + Lixo + + + + Computer + Computador + + + + Applications + Aplicacións + + + + Network + Rede + + + + Devices + Dispositivos + + + + Bookmarks + Marcadores + + + + Fm::PlacesView + + + Empty Trash + + + + Rename + Renomear + + + Delete + Eliminar + + + + Open in New Tab + + + + + Open in New Window + + + + + Move Bookmark Up + + + + + Move Bookmark Down + + + + + Rename Bookmark + + + + + Remove Bookmark + + + + + + Unmount + Desmontar + + + + Mount + Montar + + + + Eject + Expulsar + + + + Fm::RenameDialog + + + + Type: %1 +Size: %2 +Modified: %3 + Tipo: %1 +Tamaño: %2 +Modificado: %3 + + + + Type: %1 +Modified: %2 + Tipo: %1 +Modificado: %2 + + + + Type: %1 +Modified: %3 + Tipo: %1 +Modificado: %3 + + + + &Overwrite + &Sobrescribir + + + + &Rename + &Renomear + + + + Fm::SidePane + + + + Places + Lugares + + + + + Directory Tree + + + + + Shows list of common places, devices, and bookmarks in sidebar + + + + + Shows tree of directories in sidebar + + + + + MountOperationPasswordDialog + + + Mount + Montar + + + + Connect &anonymously + Conectar &anonimamente + + + + Connect as u&ser: + Conectar como u&suario + + + + &Username: + Nome de &usuario: + + + + &Password: + &Contrasinal: + + + + &Domain: + &Dominio: + + + + Forget password &immediately + Esquecer o contrasinal &inmediatamente + + + + Remember password until you &logout + Lembrar o contrasinal ata &saír da sesión + + + + Remember &forever + &Lemprar para sempre + + + + QObject + + + + + + Error + Erro + + + + Rename File + Renomear o ficheiro + + + + Please enter a new name: + Introduza un nome novo: + + + + Create Folder + + + + + Please enter a new file name: + Introduza un novo nome de ficheiro: + + + + New text file + Novo ficheiro de texto + + + + Please enter a new folder name: + Introduza un nome novo para o cartafol: + + + + New folder + Novo cartafol + + + + Enter a name for the new %1: + + + + + Create File + Crear un ficheiro + + + + RenameDialog + + + Confirm to replace files + Confirmar a substitución de ficheiros + + + + <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> + <html><head/><body><p><span style=" font-weight:600;">Xa existe un ficheiro con este mesmo nome neste lugar.</span></p><p>Quere substituír o ficheiro existente?</p></body></html> + + + + dest + dest + + + + with the following file? + co seguinte ficheiro? + + + + src file info + Ficheiro de información «src» + + + + dest file info + Ficheiro de información «dest» + + + + src + src + + + + &File name: + Nome do &ficheiro: + + + + Apply this option to all existing files + Aplicar esta opción a todos os ficheiros existentes + + + + SearchDialog + + + Search Files + + + + + Name/Location + + + + + File Name Patterns: + + + + + * + + + + + Case insensitive + + + + + Use regular expression + + + + + Places to Search: + + + + + &Add + + + + + &Remove + + + + + Search in sub directories + + + + + Search for hidden files + + + + + File Type + + + + + Only search for files of following types: + + + + + Text files + + + + + Image files + + + + + Audio files + + + + + Video files + + + + + Documents + + + + + Folders + + + + + Content + + + + + File contains: + + + + + Case insensiti&ve + + + + + &Use regular expression + + + + + Properties + Propiedades + + + + File Size: + + + + + Larger than: + + + + + + Bytes + + + + + + KiB + + + + + + MiB + + + + + + GiB + + + + + Smaller than: + + + + + Last Modified Time: + + + + + Earlier than: + + + + + Later than: + + + + diff --git a/src/translations/libfm-qt_hu.ts b/src/translations/libfm-qt_hu.ts new file mode 100644 index 0000000..41e2041 --- /dev/null +++ b/src/translations/libfm-qt_hu.ts @@ -0,0 +1,1293 @@ + + + + + AppChooserDialog + + + Choose an Application + Alkalmazás választás + + + + Installed Applications + Telepített alkalmazások + + + + Custom Command + Egyéb parancs + + + + Command line to execute: + Végrehajtandó parancs: + + + + Application name: + Alkalmazás neve: + + + + <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> + <b>A parancssorban használható speciális karakterek:</b> +<ul> +<li><b>%f</b>: Egy fájnév</li> +<li><b>%F</b>: Több fájlnév</li> +<li><b>%u</b>: Egy URI</li> +<li><b>%U</b>: Több URI</li> +</ul> + + + + Keep terminal window open after command execution + Végrehajtás után a terminál nyitva marad + + + + Execute in terminal emulator + Végrehajtás külső terminálban + + + + Set selected application as default action of this file type + A választott alkalmazás rendelődjék a fáljtípushoz + + + + EditBookmarksDialog + + + Edit Bookmarks + Könyvjelzők szerkesztése + + + + Name + Név + + + + Location + Hely + + + + &Add Item + Ho&zzáadás + + + + &Remove Item + Tö&rlés + + + + Use drag and drop to reorder the items + Húzással rendezhető minden elem + + + + ExecFileDialog + + + Execute file + Fájl futtatás + + + + &Open + &Nyitás + + + + E&xecute + &Futtatás + + + + Execute in &Terminal + Futtatás &terminálban + + + + Cancel + Mégse + + + + FileOperationDialog + + + Destination: + Cél: + + + + Processing: + Feldolgozva: + + + + Preparing... + Előkészület... + + + + Progress + Folyamat + + + + Time remaining: + Hátralévő idő: + + + + FilePropsDialog + + + File Properties + Fájljellemzők + + + + General + Általános + + + + Location: + Hely: + + + + File type: + Fájltípus: + + + + Mime type: + Mime típus: + + + + File size: + Fájlméret: + + + + On-disk size: + Mérete a lemezen: + + + + Last modified: + Módosítási idő: + + + + Link target: + Link cél: + + + + Open With: + Megnyitás ezzel: + + + + Last accessed: + Utolsó hozzáférés: + + + + Permissions + Jogosultságok + + + + Ownership + Tulajdonos + + + + + + Group: + Csoport: + + + + + + Owner: + Tulajdonos: + + + + Access Control + Hozzáférés + + + + + Other: + Mások: + + + + Make the file executable + Futtatható + + + + + + Read + Olvas + + + + + + Write + Ír + + + + + + Execute + Végrehajt + + + + Sticky + Sticky + + + + SetUID + UID beállítás + + + + SetGID + GID beállítás + + + + Advanced Mode + Haladó mód + + + + Fm::AppChooserComboBox + + + Customize + Saját beállítás + + + + Fm::AppChooserDialog + + + Select an application to open "%1" files + Alkalmazás választás a "%1" fájl megnyitásához + + + + Fm::CreateNewMenu + + + Folder + Mappa + + + + Blank File + Üres fájl + + + + Fm::DirTreeModel + + + Loading... + Olvasás... + + + + <No sub folders> + <Nincs almappa> + + + + Fm::DirTreeView + + + Open in New T&ab + Megnyi&tás új fülön + + + + Open in New Win&dow + Me&gnyitás új ablakban + + + + Open in Termina&l + Megnyitás terminá&lban + + + + Fm::DndActionMenu + + + Copy here + Másolás ide + + + + Move here + Mozgatás ide + + + + Create symlink here + Szimlink ide + + + + Cancel + Mégse + + + + Fm::EditBookmarksDialog + + + New bookmark + Új könyvjelző + + + + Fm::ExecFileDialog + + + This text file '%1' seems to be an executable script. +What do you want to do with it? + Ez a '%1' fájl futtatható szkriptnek tűnik. +Mi legyen vele? + + + + This file '%1' is executable. Do you want to execute it? + Ez a '%1' fájl futtatható. Futtassuk? + + + + Fm::FileMenu + + + Open + Nyit + + + + Create &New + &Új létrehozása + + + + &Restore + &Visszalép + + + + Cut + Kivág + + + + Copy + Másol + + + + Paste + Beilleszt + + + + + &Move to Trash + Kukába &mozgat + + + + Output + Kimenet + + + + &Delete + &Töröl + + + + Rename + Átnevez + + + + Open With... + Megnyitás ezzel... + + + + Other Applications + Más alkalmazások + + + + Extract to... + Kibontás... + + + + Extract Here + Kibontás ide + + + + Compress + Csomagolás + + + + Properties + Tulajdonságok + + + + Fm::FileOperation + + + Error + Hiba + + + + Some files cannot be moved to trash can because the underlying file systems don't support this operation. +Do you want to delete them instead? + Egyes fájlok nem mozgathatók a kukába, mert a rendszer ezt nem engedélyezi. +Töröljük őket véglegesen? + + + + + Confirm + Jóváhagyás + + + + Do you want to delete the selected files? + Töröljük a választott fájlokat? + + + + Do you want to move the selected files to trash can? + Helyezzük a kukába a választott fájlokat? + + + + Fm::FileOperationDialog + + + Move files + Fájlmozgatás + + + + Moving the following files to destination folder: + A következő fájlok mozgatása ide: + + + + Copy Files + Fájlmásolás + + + + Copying the following files to destination folder: + A következő fájlok másolása ide: + + + + Trash Files + Fájlok a kukába + + + + Moving the following files to trash can: + A következő fájlok kukába mozgatása: + + + + Delete Files + Fájltörlés + + + + Deleting the following files + A következő fájlok törlése + + + + Create Symlinks + Szimlink létrehozás + + + + Creating symlinks for the following files: + Szimlink készítés a következő fájlokra: + + + + Change Attributes + Attribútum változtatás + + + + Changing attributes of the following files: + A következő fájlok attribútum változtatása: + + + + Restore Trashed Files + Kukázott fájlok visszaállítása + + + + Restoring the following files from trash can: + A következő fájlok visszaállítása a kukából: + + + + Error + Hiba + + + + Fm::FilePropsDialog + + + View folder content + Mappatartalom + + + + View and modify folder content + Mappatartalom és változtatása + + + + Read + Olvasás + + + + Read and write + Olvasás és írás + + + + Forbidden + Tiltott + + + + Files of different types + Különféle fájltípusok + + + + Multiple Files + Többszörös fájlok + + + + Apply changes + Változtatások alkalmazása + + + + Do you want to recursively apply these changes to all files and sub-folders? + Minden mappára és fájlra alkalmazzuk a változtatásokat? + + + + Fm::FileSearchDialog + + + Error + Hiba + + + + You should add at least add one directory to search. + Kereséshez legalább egy mappa megadandó. + + + + Select a folder + Mappa választás + + + + Fm::FolderMenu + + + Create &New + &Új létrehozása + + + + &Paste + &Beilleszt + + + + Select &All + Minden kivál&asztása + + + + Invert Selection + Kiválasztás fordítása + + + + Sorting + Rendezés + + + + Show Hidden + Rejtettek is + + + + Folder Pr&operties + Mappatulajd&onságok + + + Folder + Mappa + + + Blank File + Üres fájl + + + + By File Name + Név + + + + By Modification Time + Módosítási idő + + + + By File Size + Méret + + + + By File Type + Típus + + + + By File Owner + Tulajdonos + + + + Ascending + Emelkedő + + + + Descending + Csökkenő + + + + Folder First + Mappák elől + + + + Case Sensitive + Nagybetűérzékeny + + + + Fm::FolderModel + + + Name + Név + + + + Type + Típus + + + + Size + Méret + + + + Modified + Módosítva + + + + Owner + Tulaj + + + + Fm::FontButton + + + Bold + Kövér + + + + Italic + Dőlt + + + + Fm::MountOperationPasswordDialog + + + &Connect + &Kapcsolódás + + + + Fm::PlacesModel + + + Places + Helyek + + + + Desktop + Asztal + + + + Trash + Kuka + + + + Computer + Számítógép + + + + Applications + Alkalmazások + + + + Network + Hálózat + + + + Devices + Eszközök + + + + Bookmarks + Könyvjelzők + + + + Fm::PlacesView + + + Empty Trash + Kukaürítés + + + Rename + Átnevez + + + Delete + Töröl + + + + Open in New Tab + Megnyitás új fülön + + + + Open in New Window + Megnyitás új ablakban + + + + Move Bookmark Up + Könyvjelző föl + + + + Move Bookmark Down + Könyvjelző le + + + + Rename Bookmark + Könyvjelző átnevezés + + + + Remove Bookmark + Könyvjelző törlés + + + + + Unmount + Lecsatol + + + + Mount + Csatol + + + + Eject + Kidobat + + + + Fm::RenameDialog + + + + Type: %1 +Size: %2 +Modified: %3 + Típus: %1 +Méret: %2 +Módosítva: %3 + + + + Type: %1 +Modified: %2 + Típus: %1 +Módosítva: %2 + + + + Type: %1 +Modified: %3 + Típus: %1 +Módosítva: %3 + + + + &Overwrite + &Felülír + + + + &Rename + &Átnevez + + + + Fm::SidePane + + + + Places + Helyek + + + + + Directory Tree + Könyvtárfa + + + + Shows list of common places, devices, and bookmarks in sidebar + Általános helyek, eszközök és könyvjelzők listája az oldalsávban + + + + Shows tree of directories in sidebar + Könyvtárfa az oldalsávban + + + + MountOperationPasswordDialog + + + Mount + Csatlakozás + + + + Connect &anonymously + Névtelen cs&atlakozás + + + + Connect as u&ser: + Felha&sználóként: + + + + &Username: + &Név: + + + + &Password: + &Jelszó: + + + + &Domain: + &Hálózati cím: + + + + Forget password &immediately + Jelszófelejtés &iziben + + + + Remember password until you &logout + Je&lszófelejtés kilépéskor + + + + Remember &forever + Jelszóme&gjegyzés örökre + + + + QObject + + + + + + Error + Hiba + + + + Rename File + Fájlátnevezés + + + + Please enter a new name: + Új név: + + + + Create Folder + Mappakészítés + + + + Please enter a new file name: + Új fájlnév: + + + + New text file + Új szövegfájl + + + + Please enter a new folder name: + Űj mappanév: + + + + New folder + Új mappa + + + + Enter a name for the new %1: + Az új %1 neve: + + + + Create File + Fájlkészítés + + + + RenameDialog + + + Confirm to replace files + Fájlfelülírás megerősítése + + + + <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> + <html><head/><body><p><span style=" font-weight:600;">Itt már van ilyen nevű fájl.</span></p><p>Felülírjuk a meglévőt?</p></body></html> + + + + dest + cél + + + + with the following file? + Ezzel a fájllal? + + + + src file info + forrásfájl infó + + + + dest file info + célfájl infó + + + + src + forrás + + + + &File name: + &Fájlnév: + + + + Apply this option to all existing files + Az összes fájlra vonatkozzon mindez + + + + SearchDialog + + + Search Files + Fájlkeresés + + + + Name/Location + Név/hely + + + + File Name Patterns: + Fájlnév szűrő: + + + + * + * + + + + Case insensitive + Nagybetű érzéketlen + + + + Use regular expression + Szabályos kifejezés + + + + Places to Search: + Keresési helyek: + + + + &Add + Hozzá&ad + + + + &Remove + Tö&röl + + + + Search in sub directories + Alkönyvtárakban is keres + + + + Search for hidden files + Rejtett fájlok is + + + + File Type + Fájltípus + + + + Only search for files of following types: + Csak ilyen típusú fájlokat keres: + + + + Text files + Szöveg + + + + Image files + Kép + + + + Audio files + Hang + + + + Video files + Videó + + + + Documents + Dokumentum + + + + Folders + Könyvtár + + + + Content + Tartalom + + + + File contains: + Fájl tartalmazza: + + + + Case insensiti&ve + Nagybetű ér&zéketlen + + + + &Use regular expression + Szabvá&nyos kifejezés + + + + Properties + Tulajdonságok + + + + File Size: + Méret: + + + + Larger than: + Nagyobb mint: + + + + + Bytes + Bájt + + + + + KiB + Kb + + + + + MiB + Mb + + + + + GiB + Gb + + + + Smaller than: + Kissebb mint: + + + + Last Modified Time: + Utolsó módosítás ideje: + + + + Earlier than: + Korábbi ennél: + + + + Later than: + Későbbi ennél: + + + diff --git a/src/translations/libfm-qt_it.ts b/src/translations/libfm-qt_it.ts new file mode 100644 index 0000000..168dc52 --- /dev/null +++ b/src/translations/libfm-qt_it.ts @@ -0,0 +1,1297 @@ + + + + + AppChooserDialog + + + Choose an Application + Scegli un'applicazione + + + + Installed Applications + Applicazioni installate + + + + Custom Command + Comando personalizzato + + + + Command line to execute: + Riga di comando da eseguire: + + + + Application name: + Nome dell'applicazione: + + + + <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> + <b>Questi caratteri speciali possono essere usati nella riga di comando:</b> +<ul> +<li><b>%f</b>: indica un nome file singolo</li> +<li><b>%F</b>: indica diversi nomi file</li> +<li><b>%u</b>: indica un URI singolo del file</li> +<li><b>%U</b>: indica diversi URI</li> +</ul> + + + + Keep terminal window open after command execution + Non chiudere il terminale dopo l'esecuzione + + + + Execute in terminal emulator + Esegui in un emulatore di terminale + + + + Set selected application as default action of this file type + Ricorda come associazione predefinita per questo tipo di file + + + + EditBookmarksDialog + + + Edit Bookmarks + Modifica segnalibri + + + + Name + Nome + + + + Location + Posizione + + + + &Add Item + &Aggiungi elemento + + + + &Remove Item + &Rimuovi elemento + + + + Use drag and drop to reorder the items + Trascina per riordinare gli elementi + + + + ExecFileDialog + + + Execute file + Esegui file + + + + &Open + &Apri + + + + E&xecute + E&segui + + + + Execute in &Terminal + Esegui in un &terminale + + + + Cancel + Annulla + + + + FileOperationDialog + + + Destination: + Destinazione: + + + + Processing: + Elaborazione: + + + + Preparing... + Preparazione... + + + + Progress + Avanzamento + + + + Time remaining: + Tempo rimanente: + + + + FilePropsDialog + + + File Properties + Proprietà file + + + + General + Generale + + + + Location: + Posizione: + + + + File type: + Tipo file: + + + + Mime type: + Tipo MIME: + + + + File size: + Dimensione file: + + + + On-disk size: + Dimensione sul disco: + + + + Last modified: + Ultima modifica: + + + + Link target: + Destinazione collegamento: + + + + Open With: + Apri con: + + + + Last accessed: + Ultimo accesso: + + + + Permissions + Permessi + + + + Ownership + Proprietà + + + + + + Group: + Gruppo: + + + + + + Owner: + Proprietario: + + + + Access Control + Controllo degli accessi + + + + + Other: + Altri: + + + + Make the file executable + Rendi il file eseguibile + + + + + + Read + Lettura + + + + + + Write + Scrittura + + + + + + Execute + Esecuzione + + + + Sticky + Sticky + + + + SetUID + SetUID + + + + SetGID + SetGID + + + + Advanced Mode + Modalità avanzata + + + + Fm::AppChooserComboBox + + + Customize + Personalizza + + + + Fm::AppChooserDialog + + + Select an application to open "%1" files + Seleziona un'applicazione per aprire i file "%1" + + + + Fm::CreateNewMenu + + + Folder + Cartella + + + + Blank File + File vuoto + + + + Fm::DirTreeModel + + + Loading... + Caricamento in corso... + + + + <No sub folders> + <nessuna sottocartella> + + + + Fm::DirTreeView + + + Open in New T&ab + Apri in una nuova &scheda + + + + Open in New Win&dow + Apri in una nuova &finestra + + + + Open in Termina&l + Apri in un &terminale + + + + Fm::DndActionMenu + + + Copy here + Copia qui + + + + Move here + Sposta qui + + + + Create symlink here + Crea collegamento qui + + + + Cancel + Annulla + + + + Fm::EditBookmarksDialog + + + New bookmark + Nuovo segnalibro + + + + Fm::ExecFileDialog + + + This text file '%1' seems to be an executable script. +What do you want to do with it? + Questo file di testo "%1" sembra essere uno script eseguibile. +Cosa vuoi fare? + + + + This file '%1' is executable. Do you want to execute it? + Questo file "%1" è eseguibile. Vuoi eseguirlo? + + + + Fm::FileMenu + + + Open + Apri + + + OpenWith + ApriCon + + + + Cut + Taglia + + + + Copy + Copia + + + + Paste + Incolla + + + + + &Move to Trash + Cestin&a + + + + Output + Risultato + + + + &Delete + &Elimina + + + + Rename + Rinomina + + + + Open With... + Apri con... + + + + Other Applications + Altre applicazioni + + + + Create &New + Crea &nuovo + + + + &Restore + &Ripristina + + + + Extract to... + Estrai in... + + + + Extract Here + Estrai qui + + + + Compress + Comprimi + + + + Properties + Proprietà + + + + Fm::FileOperation + + + Error + Errore + + + + Some files cannot be moved to trash can because the underlying file systems don't support this operation. +Do you want to delete them instead? + Alcuni file non possono essere spostati nel cestino perché il file system su cui si trovano non supporta questa operazione. +Vuoi invece eliminarli? + + + + + Confirm + Conferma + + + + Do you want to delete the selected files? + Vuoi eliminare i file selezionati? + + + + Do you want to move the selected files to trash can? + Vuoi spostare nel cestino i file selezionati? + + + + Fm::FileOperationDialog + + + Move files + Sposta file + + + + Moving the following files to destination folder: + Spostamento dei file seguenti nella cartella di destinazione: + + + + Copy Files + Copia file + + + + Copying the following files to destination folder: + Copia dei file seguenti nella cartella di destinazione: + + + + Trash Files + Cestina file + + + + Moving the following files to trash can: + Spostamento dei file seguenti nel cestino: + + + + Delete Files + Elimina file + + + + Deleting the following files + Eliminazione dei file seguenti + + + + Create Symlinks + Crea collegamenti simbolici + + + + Creating symlinks for the following files: + Creazione collegamenti simbolici per i seguenti file: + + + + Change Attributes + Cambia attributi + + + + Changing attributes of the following files: + Modifica degli attributi per i seguenti file: + + + + Restore Trashed Files + Ripristina file cestinati + + + + Restoring the following files from trash can: + Ripristino dei file seguenti dal cestino: + + + + Error + Errore + + + + Fm::FilePropsDialog + + + View folder content + Visualizza il contenuto della cartella + + + + View and modify folder content + Visualizza e modifica il contenuto della cartella + + + + Read + Lettura + + + + Read and write + Lettura e scrittura + + + + Forbidden + Vietato + + + + Files of different types + File di tipi diversi + + + + Multiple Files + File multipli + + + + Apply changes + Applica modifiche + + + + Do you want to recursively apply these changes to all files and sub-folders? + Vuoi applicare ricorsivamente queste modifiche a tutti i file e a tutte le sottocartelle? + + + + Fm::FileSearchDialog + + + Error + Errore + + + + You should add at least add one directory to search. + Dovresti aggiungere almeno una cartella in cui cercare. + + + + Select a folder + Seleziona una cartella + + + + Fm::FolderMenu + + + Create &New + Crea &nuovo + + + + &Paste + &Incolla + + + + Select &All + Seleziona t&utto + + + + Invert Selection + Inverti selezione + + + + Sorting + Ordinamento + + + + Show Hidden + Mostra nascosti + + + + Folder Pr&operties + Pr&oprietà cartella + + + Folder + Cartella + + + File + File + + + + By File Name + Per nome file + + + + By Modification Time + Per data modifica + + + + By File Size + Per dimensione file + + + + By File Type + Per tipo file + + + + By File Owner + Per proprietario file + + + + Ascending + Crescente + + + + Descending + Decrescente + + + + Folder First + Prima le cartelle + + + + Case Sensitive + Distingui MAIUSCOLE/minuscole + + + + Fm::FolderModel + + + Name + Nome + + + + Type + Tipo + + + + Size + Dimensione + + + + Modified + Modificato + + + + Owner + Proprietario + + + + Fm::FontButton + + + Bold + Grassetto + + + + Italic + Corsivo + + + + Fm::MountOperationPasswordDialog + + + &Connect + &Connetti + + + + Fm::PlacesModel + + + Places + Risorse + + + + Desktop + Scrivania + + + + Trash + Cestino + + + + Computer + Computer + + + + Applications + Applicazioni + + + + Network + Rete + + + + Devices + Dispositivi + + + + Bookmarks + Segnalibri + + + + Fm::PlacesView + + + Empty Trash + Svuota cestino + + + Rename + Rinomina + + + Delete + Elimina + + + + Open in New Tab + Apri in una nuova scheda + + + + Open in New Window + Apri in una nuova finestra + + + + Move Bookmark Up + Sposta segnalibro in su + + + + Move Bookmark Down + Sposta segnalibro in giù + + + + Rename Bookmark + Rinomina segnalibro + + + + Remove Bookmark + Rimuovi segnalibro + + + + + Unmount + Smonta + + + + Mount + Monta + + + + Eject + Espelli + + + + Fm::RenameDialog + + + + Type: %1 +Size: %2 +Modified: %3 + Tipo: %1 +Dimensione: %2 +Ultima modifica: %3 + + + + Type: %1 +Modified: %2 + Tipo: %1 +Ultima modifica: %2 + + + + Type: %1 +Modified: %3 + Tipo: %1 +Ultima modifica: %3 + + + + &Overwrite + S&ovrascrivi + + + + &Rename + &Rinomina + + + + Fm::SidePane + + + + Places + Risorse + + + + + Directory Tree + Albero delle cartelle + + + + Shows list of common places, devices, and bookmarks in sidebar + Mostra un elenco di risorse, dispositivi e segnalibri nel pannello laterale + + + + Shows tree of directories in sidebar + Mostra l'albero delle cartelle nel pannello laterale + + + + MountOperationPasswordDialog + + + Mount + Monta + + + + Connect &anonymously + Connetti in modo &anonimo + + + + Connect as u&ser: + Connetti come &utente: + + + + &Username: + &Nome utente: + + + + &Password: + &Password: + + + + &Domain: + &Dominio: + + + + Forget password &immediately + Dimentica la password &immediatamente + + + + Remember password until you &logout + Ricorda &la password fino al termine sessione + + + + Remember &forever + Ricorda per &sempre + + + + QObject + + + + + + Error + Errore + + + + Rename File + Rinomina file + + + + Please enter a new name: + Digita un nuovo nome: + + + + Create Folder + Crea cartella + + + + Please enter a new file name: + Digita un nuovo nome del file: + + + + New text file + Nuovo file di testo + + + + Please enter a new folder name: + Digita un nuovo nome della cartella: + + + + New folder + Nuova cartella + + + + Enter a name for the new %1: + Digita un nome per %1: + + + + Create File + Crea file + + + + RenameDialog + + + Confirm to replace files + Conferma sostituzione dei file + + + + <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> + <html><head/><body><p><span style=" font-weight:600;">C'è già un file con lo stesso nome in questa posizione.</span></p><p>Vuoi sostituire il file esistente?</p></body></html> + + + + dest + dest + + + + with the following file? + con il file seguente? + + + + src file info + info file sorg + + + + dest file info + info file dest + + + + src + sorg + + + + &File name: + Nome &file: + + + + Apply this option to all existing files + Applica questa opzione a tutti i file esistenti + + + + SearchDialog + + + Search Files + Cerca file + + + + Name/Location + Nome/Posizione + + + + File Name Patterns: + Modelli di nome dei file: + + + + * + * + + + + Case insensitive + Non distinguere le maiuscole + + + + Use regular expression + Utilizza un'espressione regolare + + + + Places to Search: + Risorse in cui cercare: + + + + &Add + &Aggiungi + + + + &Remove + &Rimuovi + + + + Search in sub directories + Cerca nelle sottocartelle + + + + Search for hidden files + Cerca i file nascosti + + + + File Type + Tipo di file + + + + Only search for files of following types: + Cerca solo i file dei tipi seguenti: + + + + Text files + File di testo + + + + Image files + File di immagini + + + + Audio files + File audio + + + + Video files + File video + + + + Documents + Documenti + + + + Folders + Cartelle + + + + Content + Contenuto + + + + File contains: + Il file contiene: + + + + Case insensiti&ve + Non disting&uere le maiuscole + + + + &Use regular expression + &Utilizza un'espressione regolare + + + + Properties + Proprietà + + + + File Size: + Dimensione file: + + + + Larger than: + Più grande di: + + + + + Bytes + Byte + + + + + KiB + KiB + + + + + MiB + MiB + + + + + GiB + GiB + + + + Smaller than: + Più piccolo di: + + + + Last Modified Time: + Ultima modifica: + + + + Earlier than: + Prima del: + + + + Later than: + Dopo del: + + + diff --git a/src/translations/libfm-qt_ja.ts b/src/translations/libfm-qt_ja.ts new file mode 100644 index 0000000..672030a --- /dev/null +++ b/src/translations/libfm-qt_ja.ts @@ -0,0 +1,1302 @@ + + + + + AppChooserDialog + + + Choose an Application + アプリケーションを選ぶ + + + + Installed Applications + インストール済アプリケーション + + + + Custom Command + カスタムコマンド + + + + Command line to execute: + 実行するコマンドライン: + + + + Application name: + アプリケーション名 + + + + <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> + <b>コマンドラインには、次の特別なコードを使用することができます:</b> +<ul> +<li><b>%f</b>: 単一のファイル名を表す</li> +<li><b>%F</b>: 複数のファイル名を表す</li> +<li><b>%u</b>: 単一のファイルのURIを表す</li> +<li><b>%U</b>: 複数のURIを表す</li> +</ul> + + + + Keep terminal window open after command execution + コマンド実行後も端末のウィンドウを閉じない + + + + Execute in terminal emulator + 端末エミュレーター内で実行 + + + + Set selected application as default action of this file type + 選択したアプリケーションをこのファイルの種類に関連付ける + + + + EditBookmarksDialog + + + Edit Bookmarks + ブックマークを編集 + + + + Name + 名前 + + + + Location + 場所 + + + + &Add Item + アイテムを追加(&A) + + + + &Remove Item + アイテムを削除(&R) + + + + Use drag and drop to reorder the items + アイテムを並べ替えるにはドラッグアンドドロップ + + + + ExecFileDialog + + + Execute file + ファイルを実行 + + + + &Open + 開く(&O) + + + + E&xecute + 実行(&E) + + + + Execute in &Terminal + 端末内で実行(&T) + + + + Cancel + キャンセル + + + + FileOperationDialog + + + Destination: + 送り先: + + + + Processing: + 処理中: + + + + Preparing... + 準備中... + + + + Progress + 進行状況 + + + + Time remaining: + 残り時間: + + + + FilePropsDialog + + + File Properties + ファイルのプロパティー + + + + General + 一般 + + + + Location: + 場所: + + + + File type: + 種類: + + + + Mime type: + MIMEタイプ: + + + + File size: + サイズ: + + + + On-disk size: + ディスク上のサイズ: + + + + Last modified: + 最終更新日時: + + + + Link target: + リンク先: + + + + Open With: + 関連付け: + + + + Last accessed: + 最終アクセス日時: + + + + Permissions + パーミッション + + + + Ownership + 所有権 + + + + + + Group: + グループ: + + + + + + Owner: + 所有者: + + + + Access Control + アクセス制限 + + + + + Other: + その他: + + + + Make the file executable + ファイルを実行可能にする + + + + + + Read + 読取り + + + + + + Write + 書込み + + + + + + Execute + 実行 + + + + Sticky + スティッキー + + + + SetUID + UIDを設定 + + + + SetGID + GIDを設定 + + + + Advanced Mode + 高度なモード + + + + Fm::AppChooserComboBox + + + Customize + カスタマイズ + + + + Fm::AppChooserDialog + + + Select an application to open "%1" files + ファイルを開くアプリケーションを選択 + + + + Fm::CreateNewMenu + + + Folder + フォルダー + + + + Blank File + 空のファイル + + + + Fm::DirTreeModel + + + Loading... + ロード中... + + + + <No sub folders> + <サブフォルダーなし> + + + + Fm::DirTreeView + + + Open in New T&ab + + + + + Open in New Win&dow + + + + + Open in Termina&l + + + + + Fm::DndActionMenu + + + Copy here + ここへコピー + + + + Move here + ここへ移動 + + + + Create symlink here + シンボリックリンクを作成 + + + + Cancel + キャンセル + + + + Fm::EditBookmarksDialog + + + New bookmark + 新規ブックマーク + + + + Fm::ExecFileDialog + + + This text file '%1' seems to be an executable script. +What do you want to do with it? + このテキストファイル '%1' は実行可能スクリプトです。 +どうしますか? + + + + This file '%1' is executable. Do you want to execute it? + このファイル '%1' は実行可能です。 +実行しますか? + + + + Fm::FileMenu + + + Open + 開く + + + OpenWith + アプリケーションで開く + + + + Cut + 切り取り + + + + Copy + コピー + + + + Paste + 貼り付け + + + + + &Move to Trash + ゴミ箱へ移動(&M) + + + + Output + 出力 + + + + &Delete + 削除(&D) + + + + Rename + 名前を変更する + + + + Open With... + アプリケーションで開く + + + + Other Applications + その他のアプリケーション + + + + Create &New + 新規作成 (&N) + + + + &Restore + + + + + Extract to... + 展開する + + + + Extract Here + ここへ展開する + + + + Compress + 圧縮する + + + + Properties + プロパティー + + + + Fm::FileOperation + + + Error + エラー + + + + Some files cannot be moved to trash can because the underlying file systems don't support this operation. +Do you want to delete them instead? + ファイルシステムのサポートがないため、ゴミ箱へ移動できないファイルがあります。 +かわりにこれらを削除しますか? + + + + + Confirm + 確認 + + + + Do you want to delete the selected files? + 選択したファイルを削除しますか? + + + + Do you want to move the selected files to trash can? + 選択したファイルをゴミ箱に移しますか? + + + + Fm::FileOperationDialog + + + Move files + ファイルを移動 + + + + Moving the following files to destination folder: + 次のファイルを対象のフォルダーへ移動: + + + + Copy Files + ファイルをコピー + + + + Copying the following files to destination folder: + 次のファイルを対象のフォルダーへコピー: + + + + Trash Files + ファイルをゴミ箱へ入れる + + + + Moving the following files to trash can: + 次のファイルをゴミ箱へ入れる: + + + + Delete Files + ファイルを削除 + + + + Deleting the following files + 以下のファイルを削除 + + + + Create Symlinks + シンボリックリンク作成 + + + + Creating symlinks for the following files: + 以下のファイルのシンボリックリンクを作成: + + + + Change Attributes + 属性を変更 + + + + Changing attributes of the following files: + 以下のファイルの属性を変更: + + + + Restore Trashed Files + ゴミ箱から戻す + + + + Restoring the following files from trash can: + 以下のファイルをゴミ箱から戻す: + + + + Error + エラー + + + + Fm::FilePropsDialog + + + View folder content + フォルダーの内容を表示 + + + + View and modify folder content + フォルダーの内容を表示・変更 + + + + Read + 読取り + + + + Read and write + 読取りおよび書込み + + + + Forbidden + 禁止 + + + + Files of different types + 異なる種類のファイル + + + + Multiple Files + 複数のファイル + + + + Apply changes + 変更を適用 + + + + Do you want to recursively apply these changes to all files and sub-folders? + 変更をすべてのファイルとサブフォルダーにも再帰的に適用しますか? + + + + Fm::FileSearchDialog + + + Error + エラー + + + + You should add at least add one directory to search. + + + + + Select a folder + + + + + Fm::FolderMenu + + + Create &New + 新規作成 (&N) + + + + &Paste + ペースト(&P) + + + + Select &All + すべてを選択(&A) + + + + Invert Selection + 選択を反転 + + + + Sorting + ソート + + + + Show Hidden + 隠しファイルの表示 + + + + Folder Pr&operties + フォルダのプロパティー(&O) + + + Folder + フォルダー + + + File + ファイル + + + Blank File + 空のファイル + + + + By File Name + 名前 + + + + By Modification Time + 更新時刻 + + + + By File Size + サイズ + + + + By File Type + 種類 + + + + By File Owner + 所有者 + + + + Ascending + 昇順 + + + + Descending + 降順 + + + + Folder First + フォルダーを先に + + + + Case Sensitive + 大文字小文字を区別 + + + + Fm::FolderModel + + + Name + 名前 + + + + Type + 種類 + + + + Size + サイズ + + + + Modified + 更新日時 + + + + Owner + 所有者 + + + + Fm::FontButton + + + Bold + 太字 + + + + Italic + 斜体 + + + + Fm::MountOperationPasswordDialog + + + &Connect + 接続&(C) + + + + Fm::PlacesModel + + + Places + 場所 + + + + Desktop + デスクトップ + + + + Trash + ゴミ箱 + + + + Computer + コンピューター + + + + Applications + アプリケーション + + + + Network + ネットワーク + + + + Devices + デバイス + + + + Bookmarks + ブックマーク + + + + Fm::PlacesView + + + Empty Trash + ゴミ箱を空にする + + + Rename + 名前を変更 + + + Delete + 削除 + + + + Open in New Tab + + + + + Open in New Window + + + + + Move Bookmark Up + + + + + Move Bookmark Down + + + + + Rename Bookmark + + + + + Remove Bookmark + + + + + + Unmount + アンマウント + + + + Mount + マウント + + + + Eject + 取出し + + + + Fm::RenameDialog + + + + Type: %1 +Size: %2 +Modified: %3 + 種類: %1 +サイズ: %2 +更新日時: %3 + + + + Type: %1 +Modified: %2 + 種類: %1 +更新日時: %2 + + + + Type: %1 +Modified: %3 + 種類: %1 +更新日時: %3 + + + + &Overwrite + 上書き(&O) + + + + &Rename + 名前を変更(&R) + + + + Fm::SidePane + + + + Places + 場所 + + + + + Directory Tree + ディレクトリーツリー + + + + Shows list of common places, devices, and bookmarks in sidebar + サイドバーに、一般的な場所およびデバイス、ブックマークのリストを表示 + + + + Shows tree of directories in sidebar + サイドバーにディレクトリーツリーを表示 + + + + MountOperationPasswordDialog + + + Mount + マウント + + + + Connect &anonymously + 匿名で接続(&A) + + + + Connect as u&ser: + ユーザーとして接続(&U) + + + + &Username: + ユーザー名(&U): + + + + &Password: + &パスワード(&P): + + + + &Domain: + &ドメイン(&D): + + + + Forget password &immediately + パスワードを記憶させない + + + + Remember password until you &logout + パスワードをログアウトするまで記憶させる + + + + Remember &forever + パスワードを恒久的に記憶させる + + + + QObject + + + + + + Error + エラー + + + + Rename File + ファイル名を変更 + + + + Please enter a new name: + 新しい名前を入力してください: + + + + Create Folder + + + + + Please enter a new file name: + 新規ファイルの名前を入力してください: + + + + New text file + 新規テキストファイル + + + + Please enter a new folder name: + 新規フォルダの名前を入力してください: + + + + New folder + 新規フォルダー + + + + Enter a name for the new %1: + 新しい %1 の名前を入力してください: + + + + Create File + ファイル作成 + + + + RenameDialog + + + Confirm to replace files + ファイルの置換えを確認 + + + + <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> + <html><head/><body><p><span style=" font-weight:600;">この場所にはすでに、同じ名前のファイルがあります。</span></p><p>既存のファイルを置き換えますか?</p></body></html> + + + + dest + 送り先 + + + + with the following file? + 以下のファイルと置き換えますか? + + + + src file info + + + + + dest file info + 送り先のファイルの情報 + + + + src + 送り元のファイルの情報 + + + + &File name: + ファイル名(&F): + + + + Apply this option to all existing files + 既存のすべてのファイルにも適用する + + + + SearchDialog + + + Search Files + + + + + Name/Location + + + + + File Name Patterns: + + + + + * + + + + + Case insensitive + + + + + Use regular expression + + + + + Places to Search: + + + + + &Add + + + + + &Remove + + + + + Search in sub directories + + + + + Search for hidden files + + + + + File Type + + + + + Only search for files of following types: + + + + + Text files + + + + + Image files + + + + + Audio files + + + + + Video files + + + + + Documents + + + + + Folders + + + + + Content + + + + + File contains: + + + + + Case insensiti&ve + + + + + &Use regular expression + + + + + Properties + プロパティー + + + + File Size: + + + + + Larger than: + + + + + + Bytes + + + + + + KiB + + + + + + MiB + + + + + + GiB + + + + + Smaller than: + + + + + Last Modified Time: + + + + + Earlier than: + + + + + Later than: + + + + diff --git a/src/translations/libfm-qt_lt_LT.ts b/src/translations/libfm-qt_lt_LT.ts new file mode 100644 index 0000000..871fa6e --- /dev/null +++ b/src/translations/libfm-qt_lt_LT.ts @@ -0,0 +1,1301 @@ + + + + + AppChooserDialog + + + Choose an Application + + + + + Installed Applications + + + + + Custom Command + + + + + Command line to execute: + + + + + Application name: + + + + + <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> + + + + + Keep terminal window open after command execution + + + + + Execute in terminal emulator + + + + + Set selected application as default action of this file type + + + + + EditBookmarksDialog + + + Edit Bookmarks + Redaguoti žymeles + + + + Name + Pavadinimas + + + + Location + Vieta + + + + &Add Item + &Pridėti elementą + + + + &Remove Item + &Pašalinti elementą + + + + Use drag and drop to reorder the items + Failų tvarkai pakeisti naudokite vilkimą + + + + ExecFileDialog + + + Execute file + + + + + &Open + + + + + E&xecute + + + + + Execute in &Terminal + + + + + Cancel + Atšaukti + + + + FileOperationDialog + + + Destination: + Paskirties vieta: + + + + Processing: + Apdorojama: + + + + Preparing... + Ruošiamasi... + + + + Progress + Eiga + + + + Time remaining: + Liko laiko: + + + + FilePropsDialog + + + File Properties + Failo savybės + + + + General + Bendra + + + + Location: + Vieta: + + + + File type: + Failo tipas: + + + + Mime type: + Mime tipas: + + + + File size: + Failo dydis: + + + + On-disk size: + Dydis diske: + + + + Last modified: + Paskutinis pakeitimas: + + + + Link target: + Nuorodos tikslas: + + + + Open With: + Atverti su: + + + + Last accessed: + Paskutinį kartą naudota: + + + + Permissions + Leidimai + + + + Ownership + Nuosavybė + + + + + + Group: + Grupė: + + + + + + Owner: + Savininkas: + + + + Access Control + Prieigos kontrolė + + + + + Other: + Kita: + + + + Make the file executable + Padaryti failą vykdomu + + + + + + Read + Skaityti + + + + + + Write + Rašyti + + + + + + Execute + Vykdyti + + + + Sticky + Lipnus + + + + SetUID + Nustatyti UID + + + + SetGID + Nustatyti GID + + + + Advanced Mode + Išsamus režimas + + + + Fm::AppChooserComboBox + + + Customize + + + + + Fm::AppChooserDialog + + + Select an application to open "%1" files + + + + + Fm::CreateNewMenu + + + Folder + Aplankas + + + + Blank File + + + + + Fm::DirTreeModel + + + Loading... + + + + + <No sub folders> + + + + + Fm::DirTreeView + + + Open in New T&ab + + + + + Open in New Win&dow + + + + + Open in Termina&l + + + + + Fm::DndActionMenu + + + Copy here + Kopijuoti čia + + + + Move here + Perkelti čia + + + Create symlink here + symlink=symbolic link + Sukurti simbolinę nuorodą + + + + Cancel + Atšaukti + + + + Create symlink here + Sukurti simbolinę nuorodą + + + + Fm::EditBookmarksDialog + + + New bookmark + Nauja žymelė + + + + Fm::ExecFileDialog + + + This text file '%1' seems to be an executable script. +What do you want to do with it? + + + + + This file '%1' is executable. Do you want to execute it? + + + + + Fm::FileMenu + + + Open + Atverti + + + OpenWith + Atverti su + + + + Cut + Iškirpti + + + + Copy + Kopijuoti + + + + Paste + Įklijuoti + + + + + &Move to Trash + &Perkelti į šiukšlinę + + + + &Delete + &Pašalinti + + + + Rename + Pervadinti + + + + Create &New + &Kurti naują + + + + Extract to... + Išskleisti į... + + + + Extract Here + Išskleisti čia + + + + Compress + Archyvuoti + + + + Properties + Savybės + + + + Open With... + + + + + Other Applications + + + + + Output + + + + + &Restore + + + + + Fm::FileOperation + + + + Confirm + Patvirtinti + + + + Do you want to delete the selected files? + Ar norite ištrinti pasirinktus failus? + + + + Do you want to move the selected files to trash can? + Ar norite perkelti pasirinktus failus į šiukšlinę? + + + + Some files cannot be moved to trash can because the underlying file systems don't support this operation. +Do you want to delete them instead? + Kai kurie failai negali būti išmesti į šiukšlinę, nes failų sistema to nepalaiko. +Ar norite vietoje to juos pašalinti visam laikui? + + + + Error + Klaida + + + + Fm::FileOperationDialog + + + Move files + Perkelti failus + + + + Moving the following files to destination folder: + Šie failai perkeliami į paskirties aplanką: + + + + Copy Files + Kopijuoti failus + + + + Copying the following files to destination folder: + Šie failai kopijuojami į paskirties aplanką: + + + + Trash Files + Išmesti failus į šiukšliadėžę + + + + Moving the following files to trash can: + Failai perkeliami į šiukšliadėžę: + + + + Delete Files + Pašalinti failus + + + + Deleting the following files + Šalinami šie failai + + + Create Symlinks + symlink=symbolic link + Sukurti simbolinę nuorodą + + + + Creating symlinks for the following files: + Šiems failams kuriamos simbolinės nuorodos: + + + + Change Attributes + Keisti atributus + + + + Changing attributes of the following files: + Keičiami šių failų atributai: + + + + Restore Trashed Files + Atstatyti ištrintus failus + + + + Restoring the following files from trash can: + Šie failai atstatomi iš šiukšliadėžės: + + + + Error + Klaida + + + + Create Symlinks + Sukurti simbolinę nuorodą + + + + Fm::FilePropsDialog + + + View folder content + Žiūrėti aplanko turinį + + + + View and modify folder content + Žiūrėti ir keisti aplanko turinį + + + + Read + Skaityti + + + + Read and write + Skaityti ir rašyti + + + + Forbidden + Uždrausta + + + + Files of different types + Skirtingų tipų failai + + + + Multiple Files + Keli failai + + + + Apply changes + Pritaikyti pakeitimus + + + + Do you want to recursively apply these changes to all files and sub-folders? + Ar norite rekursyviai pritaikyti šiuos pakeitimus visiems failams ir poaplankiams? + + + + Fm::FileSearchDialog + + + Error + Klaida + + + + You should add at least add one directory to search. + + + + + Select a folder + + + + + Fm::FolderMenu + + + Create &New + &Kurti naują + + + + &Paste + Į&klijuoti + + + + Select &All + Pasirinkti &viską + + + + Invert Selection + Invertuoti pasirinkimą + + + + Sorting + Rikiavimas + + + + Show Hidden + Rodyti paslėptus + + + + Folder Pr&operties + Failo &savybės + + + Folder + Aplankas + + + File + Failas + + + + By File Name + Pagal failo vardą + + + + By Modification Time + Pagal keitimo datą + + + + By File Size + Pagal failo dydį + + + + By File Type + Pagal failo tipą + + + + By File Owner + Pagal failo savininką + + + + Ascending + Didėjančiai + + + + Descending + Mažėjančiai + + + + Folder First + Pirmiausia aplankai + + + + Case Sensitive + Skirti raidžių dydį + + + + Fm::FolderModel + + + Name + Pavadinimas + + + + Type + Tipas + + + + Size + Dydis + + + + Modified + Pakeista + + + + Owner + Savininkas + + + + Fm::FontButton + + + Bold + Paryškintas + + + + Italic + Kursyvinis + + + + Fm::MountOperationPasswordDialog + + + &Connect + &Prisijungti + + + + Fm::PlacesModel + + + Desktop + Darbastalis + + + + Trash + Šiukšlinė + + + + Computer + Kompiuteris + + + + Applications + Programos + + + + Network + Tinklas + + + + Places + + + + + Devices + + + + + Bookmarks + + + + + Fm::PlacesView + + Rename + Pervadinti + + + Delete + Šalinami failai + + + + Open in New Tab + + + + + Open in New Window + + + + + Move Bookmark Up + + + + + Move Bookmark Down + + + + + Rename Bookmark + + + + + Remove Bookmark + + + + + + Unmount + Atjungti + + + + Mount + Prijungti + + + + Eject + Išstumti + + + + Empty Trash + + + + + Fm::RenameDialog + + + + Type: %1 +Size: %2 +Modified: %3 + Tipas: %1 +Dydis: %2 +Keista: %3 + + + + Type: %1 +Modified: %3 + Tipas: %1 +Keista: %3 + + + + &Overwrite + &Perrašyti + + + + &Rename + Pe&rvadinti + + + + Type: %1 +Modified: %2 + Tipas: %1 +Keista: %2 {1 +?} + + + + Fm::SidePane + + + + Places + + + + + + Directory Tree + + + + + Shows list of common places, devices, and bookmarks in sidebar + + + + + Shows tree of directories in sidebar + + + + + MountOperationPasswordDialog + + + Mount + Prijungti + + + + Connect &anonymously + Prisijungti &anonimiškai + + + + Connect as u&ser: + Prisijungti kaip nau&dotojui: + + + + &Username: + &Naudotojo vardas: + + + + &Password: + &Slaptažodis: + + + + &Domain: + &Domenas: + + + + Forget password &immediately + &Neįsiminti slaptažodžio + + + + Remember password until you &logout + &Atsiminti slaptažodį iki atsijungsite + + + + Remember &forever + Prisiminti &visam laikui + + + + QObject + + + + + + Error + Klaida + + + + Rename File + Pervadinti failą + + + + Please enter a new name: + Įveskite naują pavadinimą: + + + + Create Folder + + + + + Please enter a new file name: + Įveskite naują failo pavadinimą: + + + + New text file + Naujas tekstinis failas + + + + Please enter a new folder name: + Įveskite naują failo pavadinimą: + + + + New folder + Naujas aplankas + + + + Create File + Sukurti failą + + + + Enter a name for the new %1: + + + + + RenameDialog + + + Confirm to replace files + Patvirtinti failų pakeitimą + + + + <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> + <html><head/><body><p><span style=" font-weight:600;">Šiame aplanke jau yra failas tokiu pačiu pavadinimu.</span></p><p>Ar norite pakeisti esamą failą?</p></body></html> + + + + dest + paskirtis + + + + with the following file? + šiuo failu? + + + + src file info + šaltinio failo informacija + + + + dest file info + paskirties failo informacija + + + + src + šaltinis + + + + &File name: + &Failo pavadinimas: + + + + Apply this option to all existing files + Pritaikyti šią nuostatą visiems failams + + + + SearchDialog + + + Search Files + + + + + Name/Location + + + + + File Name Patterns: + + + + + * + + + + + Case insensitive + + + + + Use regular expression + + + + + Places to Search: + + + + + &Add + + + + + &Remove + + + + + Search in sub directories + + + + + Search for hidden files + + + + + File Type + + + + + Only search for files of following types: + + + + + Text files + + + + + Image files + + + + + Audio files + + + + + Video files + + + + + Documents + + + + + Folders + + + + + Content + + + + + File contains: + + + + + Case insensiti&ve + + + + + &Use regular expression + + + + + Properties + Savybės + + + + File Size: + + + + + Larger than: + + + + + + Bytes + + + + + + KiB + + + + + + MiB + + + + + + GiB + + + + + Smaller than: + + + + + Last Modified Time: + + + + + Earlier than: + + + + + Later than: + + + + diff --git a/src/translations/libfm-qt_pl.ts b/src/translations/libfm-qt_pl.ts new file mode 100644 index 0000000..6b8dc71 --- /dev/null +++ b/src/translations/libfm-qt_pl.ts @@ -0,0 +1,1293 @@ + + + + + AppChooserDialog + + + Choose an Application + Wybór programu + + + + Installed Applications + Zainstalowane programy + + + + Custom Command + Własny wiersz poleceń + + + + Command line to execute: + Wiersz poleceń do wykonania: + + + + Application name: + Nazwa programu: + + + + <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> + <b>W wierszu poleceń można użyć następujących oznaczeń:</b> +<ul> +<li><b>%f</b>: nazwa pierwszego z zaznaczonych plików</li> +<li><b>%F</b>: nazwy wszystkich zaznaczonych plików</li> +<li><b>%u</b>: ścieżka pierwszego z zaznaczonych plików</li> +<li><b>%U</b>: ścieżki wszystkich zaznaczonych plików</li> +</ul> + + + + Keep terminal window open after command execution + Zachowanie otwartego terminala po wykonaniu polecenia + + + + Execute in terminal emulator + Uruchomienie w emulatorze terminala + + + + Set selected application as default action of this file type + Ustaw wybrany program jako domyślną akcję dla tego typu plików + + + + EditBookmarksDialog + + + Edit Bookmarks + Edycja zakładek + + + + Name + Nazwa + + + + Location + Położenie + + + + &Add Item + &Dodaj + + + + &Remove Item + &Usuń + + + + Use drag and drop to reorder the items + Przeciągnij i upuść aby zmienić kolejność + + + + ExecFileDialog + + + Execute file + Uruchom + + + + &Open + &Otwórz + + + + E&xecute + &Uruchom + + + + Execute in &Terminal + Uruchom w &terminalu + + + + Cancel + Anuluj + + + + FileOperationDialog + + + Destination: + Cel: + + + + Processing: + Przetwarzanie: + + + + Preparing... + Przygotowanie... + + + + Progress + Postęp + + + + Time remaining: + Pozostały czas: + + + + FilePropsDialog + + + File Properties + Właściwości pliku + + + + General + Ogólne + + + + Location: + Położenie: + + + + File type: + Typ pliku: + + + + Mime type: + Typ mime: + + + + File size: + Rozmiar pliku: + + + + On-disk size: + Rozmiar na dysku: + + + + Last modified: + Ostatnia modyfikacja: + + + + Link target: + Położenie docelowe: + + + + Open With: + Otwieranie za pomocą: + + + + Last accessed: + Ostatni dostęp: + + + + Permissions + Uprawnienia + + + + Ownership + Właściciel + + + + + + Group: + Grupa: + + + + + + Owner: + Właściciel: + + + + Access Control + Prawa dostępu + + + + + Other: + Inni: + + + + Make the file executable + Ustaw prawo wykonywalności + + + + + + Read + Odczyt + + + + + + Write + Zapis + + + + + + Execute + Wykonanie + + + + Sticky + Sticky + + + + SetUID + SetUID + + + + SetGID + SetGID + + + + Advanced Mode + Zaawansowane + + + + Fm::AppChooserComboBox + + + Customize + Dostosuj + + + + Fm::AppChooserDialog + + + Select an application to open "%1" files + WYbierz program do otwarcia "%1" plików + + + + Fm::CreateNewMenu + + + Folder + Katalog + + + + Blank File + Pusty plik + + + + Fm::DirTreeModel + + + Loading... + Wczytywanie... + + + + <No sub folders> + <Brak katalogów> + + + + Fm::DirTreeView + + + Open in New T&ab + Otwórz w nowej k&arcie + + + + Open in New Win&dow + Otwórz w nowym o&knie + + + + Open in Termina&l + Otwórz w termina&lu + + + + Fm::DndActionMenu + + + Copy here + Kopiuj tutaj + + + + Move here + Przenieś tutaj + + + + Create symlink here + Utwórz dowiązanie symboliczne + + + + Cancel + Anuluj + + + + Fm::EditBookmarksDialog + + + New bookmark + Nowa zakładka + + + + Fm::ExecFileDialog + + + This text file '%1' seems to be an executable script. +What do you want to do with it? + Plik tekstowy '%1' wygląda na wykonywalny skrypt. +Co chcesz zrobić z tym plikiem? + + + + This file '%1' is executable. Do you want to execute it? + Plik '%1' jest wykonywalny. Czy chcesz go uruchomić? + + + + Fm::FileMenu + + + Open + Otwórz + + + + Create &New + Utwórz &nowy + + + + &Restore + &Przywróć + + + + Cut + Wytnij + + + + Copy + Kopiuj + + + + Paste + Wklej + + + + + &Move to Trash + Przenieś do &kosza + + + + Output + Wyjście + + + + &Delete + &Usuń + + + + Rename + Zmień nazwę + + + + Open With... + Otwórz za pomocą... + + + + Other Applications + Inny program + + + + Extract to... + Rozpakuj do... + + + + Extract Here + Rozpakuj tutaj + + + + Compress + Skompresuj + + + + Properties + Właściwości + + + + Fm::FileOperation + + + Error + Błąd + + + + Some files cannot be moved to trash can because the underlying file systems don't support this operation. +Do you want to delete them instead? + Niektóre pliki nie mogą zostać przeniesione do kosza ponieważ system plików nie obsługuje takiej operacji. +Czy zamiast tego usunąć te pliki? + + + + + Confirm + Potwierdź + + + + Do you want to delete the selected files? + Czy chcesz usunąć wybrane pliki? + + + + Do you want to move the selected files to trash can? + Czy chcesz przenieść wybrane pliki do kosza? + + + + Fm::FileOperationDialog + + + Move files + Przenoszenie plików + + + + Moving the following files to destination folder: + Przenoszenie następujących plików do katalogu docelowego: + + + + Copy Files + Kopiowanie plików + + + + Copying the following files to destination folder: + Kopiowanie następujących plików do katalogu docelowego: + + + + Trash Files + Przenoszenie plików do kosza + + + + Moving the following files to trash can: + Przenoszenie następujących plików do kosza: + + + + Delete Files + Usuwanie plików + + + + Deleting the following files + Usuwanie następujących plików + + + + Create Symlinks + Tworzenie dowiązań symbolicznych + + + + Creating symlinks for the following files: + Tworzenie dowiązań dla następujących plików: + + + + Change Attributes + Zmiana uprawnień + + + + Changing attributes of the following files: + Zmiana uprawnień dla następujących plików: + + + + Restore Trashed Files + Przywracanie plików z kosza + + + + Restoring the following files from trash can: + Przywracanie następujących plików z kosza: + + + + Error + Błąd + + + + Fm::FilePropsDialog + + + View folder content + Wyświetlanie zawartości + + + + View and modify folder content + Wyświetlanie i modyfikowanie zawartości + + + + Read + Odczyt + + + + Read and write + Odczyt i zapis + + + + Forbidden + Zabronione + + + + Files of different types + Pliki różnego typu + + + + Multiple Files + Wiele plików + + + + Apply changes + Zastosuj zmiany + + + + Do you want to recursively apply these changes to all files and sub-folders? + Czy chcesz rekursywnie zastosować zmiany do wszystkich plików i podkatalogów? + + + + Fm::FileSearchDialog + + + Error + Błąd + + + + You should add at least add one directory to search. + Musisz dodać przynajmniej jeden katalog. + + + + Select a folder + Wybierz katalog + + + + Fm::FolderMenu + + + Create &New + Utwórz &nowy + + + + &Paste + &Wklej + + + + Select &All + Z&aznacz wszystko + + + + Invert Selection + Odwróć zaznaczenie + + + + Sorting + Sortowanie + + + + Show Hidden + Wyświetlanie ukrytych plików + + + + Folder Pr&operties + Właściwości &katalogu + + + Folder + Katalog + + + Blank File + Pusty plik + + + + By File Name + Według nazwy + + + + By Modification Time + Według czasu modyfikacji + + + + By File Size + Według rozmiaru + + + + By File Type + Według typu + + + + By File Owner + Wedłuh właściciela pliku + + + + Ascending + Rosnąco + + + + Descending + Malejąco + + + + Folder First + Najpierw katalogi + + + + Case Sensitive + Uwzględnij wielkość liter + + + + Fm::FolderModel + + + Name + Nazwa + + + + Type + Typ + + + + Size + Rozmiar + + + + Modified + Zmodyfikowany + + + + Owner + Właściciel + + + + Fm::FontButton + + + Bold + Pogrubienie + + + + Italic + Kursywa + + + + Fm::MountOperationPasswordDialog + + + &Connect + &Podłącz + + + + Fm::PlacesModel + + + Places + Położenia + + + + Desktop + Pulpit + + + + Trash + Kosz + + + + Computer + Komputer + + + + Applications + Programy + + + + Network + Sieć + + + + Devices + Urządzenia + + + + Bookmarks + Zakładki + + + + Fm::PlacesView + + + Empty Trash + Opróżnij kosz + + + Rename + Zmień nazwę + + + Delete + Usuń + + + + Open in New Tab + Otwórz w nowej karcie + + + + Open in New Window + Otwórz w nowym oknie + + + + Move Bookmark Up + Do góry + + + + Move Bookmark Down + W dół + + + + Rename Bookmark + Zmień nazwę + + + + Remove Bookmark + Usuń + + + + + Unmount + Odmontuj + + + + Mount + Montuj + + + + Eject + Wysuń + + + + Fm::RenameDialog + + + + Type: %1 +Size: %2 +Modified: %3 + Typ: %1 +Rozmiar: %2 +Zmodyfikowany: %3 + + + + Type: %1 +Modified: %2 + Typ: %1 +Zmodyfikowany: %2 + + + + Type: %1 +Modified: %3 + Typ: %1 +Zmodyfikowany: %3 + + + + &Overwrite + &Nadpisz + + + + &Rename + &Zmień nazwę + + + + Fm::SidePane + + + + Places + Położenia + + + + + Directory Tree + Drzewo katalogów + + + + Shows list of common places, devices, and bookmarks in sidebar + Pokazuje listę miejsc, urządzeń oraz zakładek w panelu bocznym + + + + Shows tree of directories in sidebar + Pokazuje drzewo katalogów w panelu bocznym + + + + MountOperationPasswordDialog + + + Mount + Montowanie + + + + Connect &anonymously + Połącz &anonimowo + + + + Connect as u&ser: + Połącz jako &użytkownik: + + + + &Username: + &Użytkownik: + + + + &Password: + &Hasło: + + + + &Domain: + &Domena: + + + + Forget password &immediately + Zapomnij hasło &od razu + + + + Remember password until you &logout + &Pamiętaj hasło do wylogowania + + + + Remember &forever + Pamiętaj na &zawsze + + + + QObject + + + + + + Error + Błąd + + + + Rename File + Zmiana nazwy + + + + Please enter a new name: + Podaj nową nazwę: + + + + Create Folder + Utwórz katalog + + + + Please enter a new file name: + Podaj nazwę pliku: + + + + New text file + Nowy plik tekstowy + + + + Please enter a new folder name: + POdaj nazwę nowego katalogu: + + + + New folder + Nowy katalog + + + + Enter a name for the new %1: + Podaj nazwę dla pliku %1: + + + + Create File + Utwórz plik + + + + RenameDialog + + + Confirm to replace files + Potwierdź aby nadpisać pliki + + + + <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> + <html><head/><body><p><span style=" font-weight:600;">Plik o podanej nazwie już istnieje w katalogu docelowym.</span></p><p>Czy chcesz nadpisać istniejący plik?</p></body></html> + + + + dest + + + + + with the following file? + następującym plikiem? + + + + src file info + + + + + dest file info + + + + + src + + + + + &File name: + &Nazwa pliku: + + + + Apply this option to all existing files + Zastosuj do wszystkich plików + + + + SearchDialog + + + Search Files + Wyszukiwanie + + + + Name/Location + Nazwa/Lokacja + + + + File Name Patterns: + Nazwa pliku: + + + + * + * + + + + Case insensitive + Nie uwzględniaj wielkości liter + + + + Use regular expression + Użyj wyrażeń regularnych + + + + Places to Search: + Katalogi do przeszukania: + + + + &Add + Dod&aj + + + + &Remove + &Usuń + + + + Search in sub directories + Przeszukuj podkatalogi + + + + Search for hidden files + Przeszukuj pliki ukryte + + + + File Type + Typy plików + + + + Only search for files of following types: + Wyszukaj tylko pliki następujących typów: + + + + Text files + Pliki tekstowe + + + + Image files + Obrazki + + + + Audio files + Pliki audio + + + + Video files + Pliki wideo + + + + Documents + Dokumenty + + + + Folders + Katalogi + + + + Content + Zawartość + + + + File contains: + Plik zawiera: + + + + Case insensiti&ve + &Nie uwzględniaj wielkości liter + + + + &Use regular expression + &Użyj wyrażeń regularnych + + + + Properties + Właściwości + + + + File Size: + Rozmiar pliku: + + + + Larger than: + Większy niż: + + + + + Bytes + + + + + + KiB + + + + + + MiB + + + + + + GiB + + + + + Smaller than: + Mniejszy niż: + + + + Last Modified Time: + CZas ostatniej modyfikacji: + + + + Earlier than: + Wcześniej niż: + + + + Later than: + Poźniej niż: + + + diff --git a/src/translations/libfm-qt_pt.ts b/src/translations/libfm-qt_pt.ts new file mode 100644 index 0000000..f76a202 --- /dev/null +++ b/src/translations/libfm-qt_pt.ts @@ -0,0 +1,1301 @@ + + + + + AppChooserDialog + + + Choose an Application + Escolha uma aplicação + + + + Installed Applications + Aplicações instaladas + + + + Custom Command + Comando personalizado + + + + Command line to execute: + Linha de comandos a executar: + + + + Application name: + Nome da aplicação: + + + + <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> + <b>Pode utilizar os seguintes códigos na linha de comandos:</b> +<ul> +<li><b>%f</b>: é o nome de um ficheiro</li> +<li><b>%F</b>: é o nome de vários ficheiros</li> +<li><b>%u</b>: é o URI do ficheiro</li> +<li><b>%U</b>: é o URI de vários ficheiros</li> +</ul> + + + + Keep terminal window open after command execution + Manter janela de terminal aberta depois de executar o comando + + + + Execute in terminal emulator + Executar no emulador de terminal + + + + Set selected application as default action of this file type + Utilizar a aplicação selecionada como pré-definida para este tipo de ficheiro + + + + EditBookmarksDialog + + + Edit Bookmarks + Editar marcadores + + + + Name + Nome + + + + Location + Localização + + + + &Add Item + &Adicionar item + + + + &Remove Item + &Remover item + + + + Use drag and drop to reorder the items + Arraste e largue para organizar itens + + + + ExecFileDialog + + + Execute file + Executar ficheiro + + + + &Open + &Abrir + + + + E&xecute + E&xecutar + + + + Execute in &Terminal + Executar no &terminal + + + + Cancel + Cancelar + + + + FileOperationDialog + + + Destination: + Destino: + + + + Processing: + Processamento: + + + + Preparing... + Preparação... + + + + Progress + Evolução + + + + Time remaining: + Tempo restante: + + + + FilePropsDialog + + + File Properties + Propriedades do ficheiro + + + + General + Geral + + + + Location: + Localização: + + + + File type: + Tipo de ficheiro: + + + + Mime type: + Tipo MIME: + + + + File size: + Tamanho do ficheiro: + + + + On-disk size: + Tamanho no disco: + + + + Last modified: + Última modificação: + + + + Link target: + Destino da ligação: + + + + Open With: + Abrir com: + + + + Last accessed: + Último acesso: + + + + Permissions + Pemissões + + + + Ownership + Propriedade + + + + + + Group: + Grupo: + + + + + + Owner: + Dono: + + + + Access Control + Controlo de acesso + + + + + Other: + Outro: + + + + Make the file executable + Marcar como executável + + + + + + Read + Leitura + + + + + + Write + Escrita + + + + + + Execute + Execução + + + + Sticky + Fixo + + + + SetUID + Definir UID + + + + SetGID + Definir GID + + + + Advanced Mode + Modo avançado + + + + Fm::AppChooserComboBox + + + Customize + Personalizar + + + + Fm::AppChooserDialog + + + Select an application to open "%1" files + Selecione uma aplicação para abrir os ficheiros %1 + + + + Fm::CreateNewMenu + + + Folder + Pasta + + + + Blank File + Ficheiro vazio + + + + Fm::DirTreeModel + + + Loading... + A carregar... + + + + <No sub folders> + <Não existem subpastas> + + + + Fm::DirTreeView + + + Open in New T&ab + + + + + Open in New Win&dow + + + + + Open in Termina&l + + + + + Fm::DndActionMenu + + + Copy here + Copiar para aqui + + + + Move here + Mover para aqui + + + + Create symlink here + Criar ligação simbólica aqui + + + + Cancel + Cancelar + + + + Fm::EditBookmarksDialog + + + New bookmark + Novo marcador + + + + Fm::ExecFileDialog + + + This text file '%1' seems to be an executable script. +What do you want to do with it? + Parece que o ficheiro de texto %1 é um script executável. +O que pretende fazer com o ficheiro? + + + + This file '%1' is executable. Do you want to execute it? + O ficheiro %1 é um executável. Pretende executar o ficheiro? + + + + Fm::FileMenu + + + Open + Abrir + + + OpenWith + Abrir com + + + + Cut + Cortar + + + + Copy + Copiar + + + + Paste + Colar + + + + + &Move to Trash + &Mover para o lixo + + + + Output + Destino + + + + &Delete + &Eliminar + + + + Rename + Mudar nome + + + + Open With... + Abrir com... + + + + Other Applications + Outras aplicações + + + + Create &New + Criar &novo(a) + + + + &Restore + &Restaurar + + + + Extract to... + Extrair para... + + + + Extract Here + Extrair aqui + + + + Compress + Comprimir + + + + Properties + Propriedades + + + + Fm::FileOperation + + + Error + Erro + + + + Some files cannot be moved to trash can because the underlying file systems don't support this operation. +Do you want to delete them instead? + Alguns ficheiros não podem ser movidos para o lixo porque o sistema de ficheiros não suporta esta operação. +Eliminar permanentemente? + + + + + Confirm + Confirmação + + + + Do you want to delete the selected files? + Quer mesmo eliminar o(s) ficheiro(s) selecionado(s)? + + + + Do you want to move the selected files to trash can? + Quer mesmo mover o(s) ficheiro(s) selecionado(s) para o lixo? + + + + Fm::FileOperationDialog + + + Move files + Mover ficheiros + + + + Moving the following files to destination folder: + A mover os ficheiros para a pasta de destino: + + + + Copy Files + Copiar ficheiros + + + + Copying the following files to destination folder: + A copiar os ficheiros para a pasta de destino: + + + + Trash Files + Destruir ficheiros + + + + Moving the following files to trash can: + A mover os ficheiros para o lixo: + + + + Delete Files + Eliminar ficheiros + + + + Deleting the following files + A eliminar estes ficheiros + + + + Create Symlinks + Criar ligações simbólicas + + + + Creating symlinks for the following files: + A criar ligações simbólicas a estes ficheiros: + + + + Change Attributes + Alterar atributos + + + + Changing attributes of the following files: + A alterar os atributos destes ficheiros: + + + + Restore Trashed Files + Restaurar ficheiros eliminados + + + + Restoring the following files from trash can: + Restaurar estes ficheiros do lixo: + + + + Error + Erro + + + + Fm::FilePropsDialog + + + View folder content + Ver conteúdo da pasta + + + + View and modify folder content + Ver e modificar conteúdo da pasta + + + + Read + Leitura + + + + Read and write + Leitura e escrita + + + + Forbidden + Proibido + + + + Files of different types + Ficheiros de outro tipo + + + + Multiple Files + Vários ficheiros + + + + Apply changes + Aplicar alterações + + + + Do you want to recursively apply these changes to all files and sub-folders? + Pretende aplicar as alterações a todos os ficheiros e subpastas? + + + + Fm::FileSearchDialog + + + Error + Erro + + + + You should add at least add one directory to search. + + + + + Select a folder + + + + + Fm::FolderMenu + + + Create &New + Criar &novo(a) + + + + &Paste + Co&lar + + + + Select &All + Selecion&ar tudo + + + + Invert Selection + Inverter seleção + + + + Sorting + Ordenação + + + + Show Hidden + Mostrar ocultos + + + + Folder Pr&operties + Pr&opriedades da pasta + + + Folder + Pasta + + + File + Ficheiro + + + Blank File + Ficheiro vazio + + + + By File Name + Por nome de ficheiro + + + + By Modification Time + Por data de modificação + + + + By File Size + Por tamanho de ficheiro + + + + By File Type + Por tipo de ficheiro + + + + By File Owner + Por dono de ficheiro + + + + Ascending + Ascendente + + + + Descending + Descendente + + + + Folder First + Pastas no início + + + + Case Sensitive + Diferenciar capitalização + + + + Fm::FolderModel + + + Name + Nome + + + + Type + Tipo + + + + Size + Tamanho + + + + Modified + Modificado + + + + Owner + Dono + + + + Fm::FontButton + + + Bold + Negrito + + + + Italic + Itálico + + + + Fm::MountOperationPasswordDialog + + + &Connect + &Ligar + + + + Fm::PlacesModel + + + Places + Locais + + + + Desktop + Área de trabalho + + + + Trash + Lixo + + + + Computer + Computador + + + + Applications + Aplicações + + + + Network + Rede + + + + Devices + Dispositivos + + + + Bookmarks + Marcadores + + + + Fm::PlacesView + + + Empty Trash + Esvaziar lixo + + + Rename + Mudar nome + + + Delete + Eliminar + + + + Open in New Tab + + + + + Open in New Window + + + + + Move Bookmark Up + + + + + Move Bookmark Down + + + + + Rename Bookmark + + + + + Remove Bookmark + + + + + + Unmount + Desmontar + + + + Mount + Montar + + + + Eject + Ejetar + + + + Fm::RenameDialog + + + + Type: %1 +Size: %2 +Modified: %3 + Tipo: %1 +Tamanho: %2 +Modificado: %3 + + + + Type: %1 +Modified: %2 + Tipo: %1 +Modificado: %2 + + + + Type: %1 +Modified: %3 + Tipo: %1 +Modificado: %3 + + + + &Overwrite + &Substituir + + + + &Rename + &Mudar nome + + + + Fm::SidePane + + + + Places + Locais + + + + + Directory Tree + Árvore de diretórios + + + + Shows list of common places, devices, and bookmarks in sidebar + Mostra a lista de locais, dispositivos e marcadores na barra lateral + + + + Shows tree of directories in sidebar + Mostra a árvore de diretórios na barra lateral + + + + MountOperationPasswordDialog + + + Mount + Montar + + + + Connect &anonymously + Ligar &anonimamente + + + + Connect as u&ser: + Ligar com utili&zador: + + + + &Username: + Nome de &utilizador: + + + + &Password: + &Senha: + + + + &Domain: + &Domínio: + + + + Forget password &immediately + Esquecer senha &imediatamente + + + + Remember password until you &logout + Memorizar sen&ha até fechar a sessão + + + + Remember &forever + Memorizar &eternamente + + + + QObject + + + + + + Error + Erro + + + + Rename File + Mudar nome do ficheiro + + + + Please enter a new name: + Introduza o novo nome: + + + + Create Folder + + + + + Please enter a new file name: + Introduza o nome do ficheiro: + + + + New text file + Novo ficheiro de texto + + + + Please enter a new folder name: + Introduza o nome da pasta: + + + + New folder + Nova pasta + + + + Enter a name for the new %1: + Introduza o nome para %1: + + + + Create File + Criar ficheiro + + + + RenameDialog + + + Confirm to replace files + Confirmação de substituição + + + + <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> + <html><head/><body><p><span style=" font-weight:600;">Já existe um ficheiro com o mesmo nome nesta localização.</span></p><p>Pretende substituir o ficheiro existente</p></body></html> + + + + dest + destino + + + + with the following file? + por este? + + + + src file info + informações do ficheiro de origem + + + + dest file info + informações do ficheiro de destino + + + + src + origem + + + + &File name: + Nome do &ficheiro: + + + + Apply this option to all existing files + Aplicar opção a todos os ficheiros existentes + + + + SearchDialog + + + Search Files + + + + + Name/Location + + + + + File Name Patterns: + + + + + * + + + + + Case insensitive + + + + + Use regular expression + + + + + Places to Search: + + + + + &Add + + + + + &Remove + + + + + Search in sub directories + + + + + Search for hidden files + + + + + File Type + + + + + Only search for files of following types: + + + + + Text files + + + + + Image files + + + + + Audio files + + + + + Video files + + + + + Documents + + + + + Folders + + + + + Content + + + + + File contains: + + + + + Case insensiti&ve + + + + + &Use regular expression + + + + + Properties + Propriedades + + + + File Size: + + + + + Larger than: + + + + + + Bytes + + + + + + KiB + + + + + + MiB + + + + + + GiB + + + + + Smaller than: + + + + + Last Modified Time: + + + + + Earlier than: + + + + + Later than: + + + + diff --git a/src/translations/libfm-qt_ru.ts b/src/translations/libfm-qt_ru.ts new file mode 100644 index 0000000..b6a45d8 --- /dev/null +++ b/src/translations/libfm-qt_ru.ts @@ -0,0 +1,1277 @@ + + + + + AppChooserDialog + + + Choose an Application + Выбрать приложение + + + + Installed Applications + Установленные приложения + + + + Custom Command + Своя команда + + + + Command line to execute: + Выполняемая командная строка: + + + + Application name: + Имя приложения: + + + + <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> + <b>Эти спец. коды могут быть использованы в командной строке:</b> +<ul> +<li><b>%f</b>: Означает имя одного файла</li> +<li><b>%F</b>: Означает имена нескольких файлов</li> +<li><b>%u</b>: Означает один URI файла</li> +<li><b>%U</b>: Означает несколько URI файлов</li> +</ul> + + + + Keep terminal window open after command execution + Держать окно терминала открытым после выполнения команды + + + + Execute in terminal emulator + Запустить в эмуляторе терминала + + + + Set selected application as default action of this file type + Использовать выбранное приложение как действие по умолчанию для файлов этого типа + + + + EditBookmarksDialog + + + Edit Bookmarks + Изменить закладки + + + + Name + Имя + + + + Location + Расположение + + + + &Add Item + &Добавить элемент + + + + &Remove Item + &Удалить элемент + + + + Use drag and drop to reorder the items + Перетаскивайте элементы мышкой, чтобы изменить их порядок + + + + ExecFileDialog + + + Execute file + Выполнить файл + + + + &Open + &Открыть + + + + E&xecute + В&ыполнить + + + + Execute in &Terminal + Выполнить в &терминале + + + + Cancel + Отмена + + + + FileOperationDialog + + + Destination: + Назначение: + + + + Processing: + Обработка: + + + + Preparing... + Подготовка... + + + + Progress + Прогресс + + + + Time remaining: + Времени осталось: + + + + FilePropsDialog + + + File Properties + Свойства файла + + + + General + Общие + + + + Location: + Расположение: + + + + File type: + Тип файла: + + + + Mime type: + Тип Mime: + + + + File size: + Размер файла: + + + + On-disk size: + Размер на диске: + + + + Last modified: + Последнее изменение: + + + + Link target: + Цель ссылки: + + + + Open With: + Открыть с помощью: + + + + Last accessed: + Последний доступ: + + + + Permissions + Разрешения + + + + Ownership + Владение + + + + + + Group: + Группа: + + + + + + Owner: + Владелец: + + + + Access Control + Контроль доступа + + + + + Other: + Остальные: + + + + Make the file executable + Сделать файл исполняемым + + + + + + Read + Чтение + + + + + + Write + Запись + + + + + + Execute + Выполнение + + + + Sticky + + + + + SetUID + + + + + SetGID + + + + + Advanced Mode + Расширенный режим + + + + Fm::AppChooserComboBox + + + Customize + Настроить + + + + Fm::AppChooserDialog + + + Select an application to open "%1" files + Выберите приложение, чтобы открыть "%1" файлов + + + + Fm::CreateNewMenu + + + Folder + Папку + + + + Blank File + Пустой файл + + + + Fm::DirTreeModel + + + Loading... + Загрузка... + + + + <No sub folders> + <Нет подпапок> + + + + Fm::DirTreeView + + + Open in New T&ab + Открыть в новой вкл&адке + + + + Open in New Win&dow + Открыть в новом о&кне + + + + Open in Termina&l + Открыть в термина&ле + + + + Fm::DndActionMenu + + + Copy here + Копировать сюда + + + + Move here + Переместить сюда + + + + Create symlink here + Создать здесь ссылку + + + + Cancel + Отмена + + + + Fm::EditBookmarksDialog + + + New bookmark + Новая закладка + + + + Fm::ExecFileDialog + + + This text file '%1' seems to be an executable script. +What do you want to do with it? + Этот текстовый файл '%1' похож на исполняемый скрипт. +Что вы хотите с ним сделать? + + + + This file '%1' is executable. Do you want to execute it? + Файл '%1' является исполняемым. Вы хотите запустить его? + + + + Fm::FileMenu + + + Open + Открыть + + + + Open With... + Открыть с помощью... + + + + Other Applications + Другие приложения + + + + Create &New + &Создать + + + + &Restore + &Восстановить + + + + Cut + Вырезать + + + + Copy + Копировать + + + + Paste + Вставить + + + + + &Move to Trash + &Переместить в корзину + + + + Rename + Переименовать + + + + Extract to... + Распаковать в... + + + + Extract Here + Распаковать здесь + + + + Compress + Сжать + + + + Properties + Свойства + + + + Output + Вывод + + + + &Delete + &Удалить + + + + Fm::FileOperation + + + Error + Ошибка + + + + Some files cannot be moved to trash can because the underlying file systems don't support this operation. +Do you want to delete them instead? + Некоторые файлы не могут быть помещены в корзину, поскольку файловая система не поддерживает эту операцию. +Вы хотите удалить их вместо перемещения? + + + + + Confirm + Подтвердить + + + + Do you want to delete the selected files? + Вы действительно хотите удалить выбранные файлы? + + + + Do you want to move the selected files to trash can? + Вы действительно хотите переместить выбранные файлы в корзину? + + + + Fm::FileOperationDialog + + + Move files + Перемещение файлов + + + + Moving the following files to destination folder: + Перемещение следующих файлов в папку назначения: + + + + Copy Files + Копирование файлов + + + + Copying the following files to destination folder: + Копирование следующих файлов в папку назначения: + + + + Trash Files + Перемещение файлов в корзину + + + + Moving the following files to trash can: + Перемещение следующих файлов в корзину: + + + + Delete Files + Удаление файлов + + + + Deleting the following files + Удаление следующих файлов + + + + Create Symlinks + Создание ссылок + + + + Creating symlinks for the following files: + Создание ссылок на следующие файлы: + + + + Change Attributes + Изменение атрибутов + + + + Changing attributes of the following files: + Изменение атрибутов следующих файлов: + + + + Restore Trashed Files + Восстановление файлов из корзины + + + + Restoring the following files from trash can: + Восстановление следующих файлов из корзины: + + + + Error + Ошибка + + + + Fm::FilePropsDialog + + + View folder content + Просмотр содержимого папки + + + + View and modify folder content + Просмотр и изменение содержимого папки + + + + Read + Чтение + + + + Read and write + Чтение и запись + + + + Forbidden + Запрещено + + + + Files of different types + Файлы разного типа + + + + Multiple Files + Несколько файлов + + + + Apply changes + Применить изменения + + + + Do you want to recursively apply these changes to all files and sub-folders? + Вы хотите применить изменения рекурсивно ко всем файлам и подпапкам? + + + + Fm::FileSearchDialog + + + Error + Ошибка + + + + You should add at least add one directory to search. + Вы должны добавить не менее одного места для поиска. + + + + Select a folder + Выберите папку + + + + Fm::FolderMenu + + + Create &New + &Создать + + + + &Paste + &Вставить + + + + Select &All + Выбрать &всё + + + + Invert Selection + Инвертировать выделение + + + + Sorting + Сортировка + + + + Show Hidden + Показать скрытые + + + + Folder Pr&operties + &Свойства папки + + + + By File Name + По имени + + + + By Modification Time + По времени изменения + + + + By File Size + По размеру + + + + By File Type + По типу + + + + By File Owner + По владельцу + + + + Ascending + Возрастающая + + + + Descending + Убывающая + + + + Folder First + Сначала папки + + + + Case Sensitive + Чувствительность к регистру + + + + Fm::FolderModel + + + Name + Имя + + + + Type + Тип + + + + Size + Размер + + + + Modified + Изменён + + + + Owner + Владелец + + + + Fm::FontButton + + + Bold + Жирный + + + + Italic + Курсив + + + + Fm::MountOperationPasswordDialog + + + &Connect + &Соединить + + + + Fm::PlacesModel + + + Places + Места + + + + Desktop + Рабочий стол + + + + Computer + Компьютер + + + + Applications + Приложения + + + + Network + Сеть + + + + Devices + Устройства + + + + Bookmarks + Закладки + + + + Trash + Корзина + + + + Fm::PlacesView + + + Empty Trash + Очистить корзину + + + + Open in New Tab + Открыть в новой вкладке + + + + Open in New Window + Открыть в новом окне + + + + Move Bookmark Up + Сдвинуть закладку вверх + + + + Move Bookmark Down + Сдвинуть закладку вниз + + + + Rename Bookmark + Переименовать закладку + + + + Remove Bookmark + Удалить закладку + + + + + Unmount + Отключить + + + + Mount + Подключить + + + + Eject + Извлечь + + + + Fm::RenameDialog + + + + Type: %1 +Size: %2 +Modified: %3 + Тип: %1 +Размер: %2 +Изменён: %3 + + + + Type: %1 +Modified: %2 + Тип: %1 +Изменён: %2 + + + + Type: %1 +Modified: %3 + Тип: %1 +Изменён: %3 + + + + &Overwrite + &Перезаписать + + + + &Rename + &Переименовать + + + + Fm::SidePane + + + + Places + Места + + + + + Directory Tree + Дерево папок + + + + Shows list of common places, devices, and bookmarks in sidebar + Показывает список обычных мест, устройств и закладок в боковой панели + + + + Shows tree of directories in sidebar + Показывает дерево папок в боковой панели + + + + MountOperationPasswordDialog + + + Mount + Подключить + + + + Connect &anonymously + Подсоединить &анонимно + + + + Connect as u&ser: + Подсоединить как &пользователь: + + + + &Username: + &Имя пользователя: + + + + &Password: + &Пароль: + + + + &Domain: + &Домен: + + + + Forget password &immediately + Забыть пароль &сразу + + + + Remember password until you &logout + Запомнить пароль пока вы не &вышли + + + + Remember &forever + Запомнить &навсегда + + + + QObject + + + + + + Error + Ошибка + + + + Rename File + Переименовать файл + + + + Please enter a new name: + Введите новое имя: + + + + Create Folder + Создать папку + + + + Please enter a new file name: + Введите имя нового файла: + + + + New text file + Новый текстовый файл + + + + Please enter a new folder name: + Введите имя новой папки: + + + + New folder + Новая папка + + + + Enter a name for the new %1: + Введите имя для нового %1: + + + + Create File + Создать файл + + + + RenameDialog + + + Confirm to replace files + Подтвердить замену файлов + + + + <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> + <html><head/><body><p><span style=" font-weight:600;">В этом месте назначения уже есть файл с таким именем.</span></p><p>Вы хотите заменить существующий файл?</p></body></html> + + + + dest + + + + + with the following file? + следующим файлом? + + + + src file info + + + + + dest file info + + + + + src + + + + + &File name: + &Имя файла: + + + + Apply this option to all existing files + Запомнить выбор для всех уже существующих файлов + + + + SearchDialog + + + Search Files + Искать файлы + + + + Name/Location + Имя/Расположение + + + + File Name Patterns: + Шаблоны имени файла: + + + + * + + + + + Case insensitive + Нечувствительный к регистру поиск + + + + Use regular expression + Использовать регулярные выражения + + + + Places to Search: + Места для поиска: + + + + &Add + &Добавить + + + + &Remove + &Удалить + + + + Search in sub directories + Искать в подпапках + + + + Search for hidden files + Искать скрытые файлы + + + + File Type + Тип файла + + + + Only search for files of following types: + Искать файлы только следующих типов: + + + + Text files + Текстовые файлы + + + + Image files + Изображения + + + + Audio files + Аудиофайлы + + + + Video files + Видеофайлы + + + + Documents + Документы + + + + Folders + Папки + + + + Content + Содержание + + + + File contains: + Файл содержит: + + + + Case insensiti&ve + Нечувствительн&ый к регистру поиск + + + + &Use regular expression + &Использовать регулярные выражения + + + + Properties + Свойства + + + + File Size: + Размер файла: + + + + Larger than: + Больше, чем: + + + + + Bytes + Байт + + + + + KiB + КиБ + + + + + MiB + МиБ + + + + + GiB + ГиБ + + + + Smaller than: + Меньше, чем: + + + + Last Modified Time: + Время последнего изменения: + + + + Earlier than: + Раньше, чем: + + + + Later than: + Позже, чем: + + + diff --git a/src/translations/libfm-qt_zh_TW.ts b/src/translations/libfm-qt_zh_TW.ts new file mode 100644 index 0000000..26deb47 --- /dev/null +++ b/src/translations/libfm-qt_zh_TW.ts @@ -0,0 +1,1305 @@ + + + + + AppChooserDialog + + + Choose an Application + 選擇一個應用程式 + + + + Installed Applications + 安裝的應用程式 + + + + Custom Command + 自訂指令 + + + + Command line to execute: + 要執行的命令列: + + + + Application name: + 應用程式名稱: + + + + <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> + <b>這些特殊代號可以在指令列參數使用:</b> +<ul> +<li><b>%f</b>:代表單一檔案名稱</li> +<li><b>%F</b>:代表多個檔案名稱</li> +<li><b>%u</b>:代表單一檔案 URI</li> +<li><b>%U</b>:代表多個 URI</li> +</ul> + + + + Keep terminal window open after command execution + 執行指令後保持終端機視窗開啟 + + + + Execute in terminal emulator + 在終端機模擬器執行 + + + + Set selected application as default action of this file type + 將所選應用程式設定為此類型檔案的預設處理程式 + + + + EditBookmarksDialog + + + Edit Bookmarks + 編輯書籤 + + + + Name + 名稱 + + + + Location + 位置 + + + + &Add Item + 新增項目(&A) + + + + &Remove Item + 移除項目(&R) + + + + Use drag and drop to reorder the items + 使用拖放重新排序項目 + + + + ExecFileDialog + + + Execute file + 執行檔案 + + + + &Open + 開啟(&) + + + + E&xecute + 執行(&X) + + + + Execute in &Terminal + 在終端機內執行(&T) + + + + Cancel + 取消 + + + + FileOperationDialog + + + Destination: + 目的地: + + + + Processing: + 正在處理: + + + + Preparing... + 準備中... + + + + Progress + 進度 + + + + Time remaining: + 剩餘時間: + + + + FilePropsDialog + + + File Properties + 檔案屬性 + + + + General + 一般 + + + + Location: + 位置: + + + + File type: + 檔案類型: + + + + Mime type: + Mime 類型: + + + + File size: + 檔案大小: + + + + On-disk size: + 磁碟上大小: + + + + Last modified: + 最後修改: + + + + Link target: + 連結目標: + + + + Open With: + 開啟: + + + + Last accessed: + 最後存取: + + + + Permissions + 權限 + + + + Ownership + 所有權 + + + + + + Group: + 群組: + + + + + + Owner: + 擁有者: + + + + Access Control + 存取控制 + + + + + Other: + 其他: + + + + Make the file executable + 使檔案可執行 + + + + + + Read + 讀取 + + + + + + Write + 寫入 + + + + + + Execute + 執行 + + + + Sticky + + + + + SetUID + + + + + SetGID + + + + + Advanced Mode + 進階模式 + + + + Fm::AppChooserComboBox + + + Customize + 自訂 + + + + Fm::AppChooserDialog + + + Select an application to open "%1" files + 選取用來開啟「%1」檔案的應用程式 + + + + Fm::CreateNewMenu + + + Folder + 資料夾 + + + + Blank File + 空白檔案 + + + + Fm::DirTreeModel + + + Loading... + 載入中... + + + + <No sub folders> + <沒有子資料夾> + + + + Fm::DirTreeView + + + Open in New T&ab + + + + + Open in New Win&dow + + + + + Open in Termina&l + + + + + Fm::DndActionMenu + + + Copy here + 複製到這裡 + + + + Move here + 移動到這裡 + + + + Create symlink here + 建立符號連結到這裡 + + + + Cancel + 取消 + + + + Fm::EditBookmarksDialog + + + New bookmark + 新書籤 + + + + Fm::ExecFileDialog + + + This text file '%1' seems to be an executable script. +What do you want to do with it? + 這個文字檔 '%1' 似乎是可執行的 script。 +想要進行什麼操作? + + + + This file '%1' is executable. Do you want to execute it? + 這個檔案 '%1' 是可執行檔,是否想要執行? + + + + Fm::FileMenu + + + Open + 開啟 + + + OpenWith + 用其他程式開啟 + + + + Open With... + 用其他程式開啟... + + + + Other Applications + 其他應用程式 + + + + Create &New + 新建(&N) + + + + &Restore + 恢復(&R) + + + + Cut + 剪下 + + + + Copy + 複製 + + + + Paste + 貼上 + + + + + &Move to Trash + 移動到垃圾桶(&M) + + + + Output + 輸出 + + + + &Delete + 刪除(&D) + + + Delete + 刪除 + + + + Rename + 重新命名 + + + + Extract to... + 解壓縮到... + + + + Extract Here + 在此解壓縮 + + + + Compress + 壓縮 + + + + Properties + 屬性 + + + + Fm::FileOperation + + + Error + 錯誤 + + + + Some files cannot be moved to trash can because the underlying file systems don't support this operation. +Do you want to delete them instead? + 因為檔案系統不支援,有些檔案無法丟到垃圾桶 +是否直接刪除這些檔案? + + + + + Confirm + 確認 + + + + Do you want to delete the selected files? + 你確定要刪除選取的檔案嗎? + + + + Do you want to move the selected files to trash can? + 你確定要把選取的檔案移到垃圾桶嗎? + + + + Fm::FileOperationDialog + + + Move files + 移動檔案 + + + + Moving the following files to destination folder: + 正在移動下列檔案到目的資料夾: + + + + Copy Files + 複製檔案 + + + + Copying the following files to destination folder: + 正在複製下列檔案到目的資料夾: + + + + Trash Files + 將檔案丟到垃圾桶 + + + + Moving the following files to trash can: + 正在將下列檔案移動到垃圾桶: + + + + Delete Files + 刪除檔案 + + + + Deleting the following files + 正在刪除下列檔案 + + + + Create Symlinks + 建立符號連結 + + + + Creating symlinks for the following files: + 正在建立下列檔案的符號連結: + + + + Change Attributes + 改變屬性 + + + + Changing attributes of the following files: + 正在更改下列檔案的屬性: + + + + Restore Trashed Files + 恢復被刪除的檔案 + + + + Restoring the following files from trash can: + 正在恢復下列被刪除的檔案 + + + + Error + 錯誤 + + + + Fm::FilePropsDialog + + + View folder content + 檢視資料夾內容 + + + + View and modify folder content + 檢視及修改資料夾內容 + + + + Read + 讀取 + + + + Read and write + 讀取及寫入 + + + + Forbidden + 禁止 + + + + Files of different types + 不同類型的檔案 + + + + Multiple Files + 多個檔案 + + + + Apply changes + 套用變更 + + + + Do you want to recursively apply these changes to all files and sub-folders? + 你是否想將這些變更套用到所有子資料夾和其內的檔案? + + + + Fm::FileSearchDialog + + + Error + 錯誤 + + + + You should add at least add one directory to search. + + + + + Select a folder + + + + + Fm::FolderMenu + + + Create &New + 新建(&N) + + + + &Paste + 貼上(&P) + + + + Select &All + 全選(&A) + + + + Invert Selection + 反向選取 + + + + Sorting + 排序 + + + + Show Hidden + 顯示隱藏檔 + + + + Folder Pr&operties + 資料夾屬性(&O) + + + Folder + 資料夾 + + + File + 檔案 + + + Blank File + 空白檔案 + + + + By File Name + 依照檔名 + + + + By Modification Time + 依照修改時間 + + + + By File Size + 依照檔案大小 + + + + By File Type + 依照檔案型態 + + + + By File Owner + 依照檔案所有者 + + + + Ascending + 升冪排列 + + + + Descending + 降冪排列 + + + + Folder First + 資料夾優先 + + + + Case Sensitive + 區分大小寫 + + + + Fm::FolderModel + + + Name + 名稱 + + + + Type + 類型 + + + + Size + 大小 + + + + Modified + 修改 + + + + Owner + 所有者 + + + + Fm::FontButton + + + Bold + 粗體 + + + + Italic + 斜體 + + + + Fm::MountOperationPasswordDialog + + + &Connect + 連接(&C) + + + + Fm::PlacesModel + + + Places + 位置 + + + + Desktop + 桌面 + + + + Trash + 垃圾桶 + + + + Computer + 電腦 + + + + Applications + 應用程式 + + + + Network + 網路 + + + + Devices + 裝置 + + + + Bookmarks + 書籤 + + + + Fm::PlacesView + + + Empty Trash + 清空垃圾桶 + + + Rename + 重新命名 + + + Delete + 刪除 + + + + Open in New Tab + + + + + Open in New Window + + + + + Move Bookmark Up + + + + + Move Bookmark Down + + + + + Rename Bookmark + + + + + Remove Bookmark + + + + + + Unmount + 卸載 + + + + Mount + 掛載 + + + + Eject + 退出 + + + + Fm::RenameDialog + + + + Type: %1 +Size: %2 +Modified: %3 + 類型: %1 +大小: %2 +最後修改: %3 + + + + Type: %1 +Modified: %2 + 類型: %1 +最後修改: %2 + + + + Type: %1 +Modified: %3 + 類型: %1 +最後修改: %3 + + + + &Overwrite + 覆蓋(&O) + + + + &Rename + 重新命名(&R) + + + + Fm::SidePane + + + + Places + 位置 + + + + + Directory Tree + 目錄樹 + + + + Shows list of common places, devices, and bookmarks in sidebar + + + + + Shows tree of directories in sidebar + + + + + MountOperationPasswordDialog + + + Mount + 掛載 + + + + Connect &anonymously + 匿名連線(&A) + + + + Connect as u&ser: + 以使用者帳號連線(&S): + + + + &Username: + 使用者名稱(&U): + + + + &Password: + 密碼(&P): + + + + &Domain: + + + + + Forget password &immediately + 立刻忘記密碼(&I) + + + + Remember password until you &logout + 記住密碼直到登出(&L) + + + + Remember &forever + 永遠記住密碼(&F) + + + + QObject + + + + + + Error + 錯誤 + + + + Rename File + 重新命名 + + + + Please enter a new name: + 請輸入一個新名稱: + + + + Create Folder + + + + + Please enter a new file name: + 請輸入一個新檔名: + + + + Please enter a new folder name: + 請輸入一個新資料夾名稱: + + + + New folder + 新資料夾 + + + + New text file + 新文字檔 + + + + Enter a name for the new %1: + 幫新的 %1 輸入一個名稱: + + + + Create File + 建立檔案 + + + + RenameDialog + + + Confirm to replace files + 確認取代檔案 + + + + <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> + + + + + dest + + + + + with the following file? + + + + + src file info + + + + + dest file info + + + + + src + + + + + &File name: + 檔名(&F): + + + + Apply this option to all existing files + 套用這個選項到所有已存在的檔案 + + + + SearchDialog + + + Search Files + + + + + Name/Location + + + + + File Name Patterns: + + + + + * + + + + + Case insensitive + + + + + Use regular expression + + + + + Places to Search: + + + + + &Add + + + + + &Remove + + + + + Search in sub directories + + + + + Search for hidden files + + + + + File Type + + + + + Only search for files of following types: + + + + + Text files + + + + + Image files + + + + + Audio files + + + + + Video files + + + + + Documents + + + + + Folders + + + + + Content + + + + + File contains: + + + + + Case insensiti&ve + + + + + &Use regular expression + + + + + Properties + 屬性 + + + + File Size: + + + + + Larger than: + + + + + + Bytes + + + + + + KiB + + + + + + MiB + + + + + + GiB + + + + + Smaller than: + + + + + Last Modified Time: + + + + + Earlier than: + + + + + Later than: + + + + diff --git a/src/utilities.cpp b/src/utilities.cpp new file mode 100644 index 0000000..8823d60 --- /dev/null +++ b/src/utilities.cpp @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "utilities.h" +#include "utilities_p.h" +#include +#include +#include +#include +#include +#include +#include +#include "fileoperation.h" +#include + +#include +#include +#include +#include + +namespace Fm { + +FmPathList* pathListFromQUrls(QList urls) { + QList::const_iterator it; + FmPathList* pathList = fm_path_list_new(); + + for(it = urls.begin(); it != urls.end(); ++it) { + QUrl url = *it; + FmPath* path = fm_path_new_for_uri(url.toString().toUtf8()); + fm_path_list_push_tail(pathList, path); + fm_path_unref(path); + } + + return pathList; +} + +void pasteFilesFromClipboard(FmPath* destPath, QWidget* parent) { + QClipboard* clipboard = QApplication::clipboard(); + const QMimeData* data = clipboard->mimeData(); + bool isCut = false; + FmPathList* paths = NULL; + + if(data->hasFormat("x-special/gnome-copied-files")) { + // Gnome, LXDE, and XFCE + QByteArray gnomeData = data->data("x-special/gnome-copied-files"); + char* pdata = gnomeData.data(); + char* eol = strchr(pdata, '\n'); + + if(eol) { + *eol = '\0'; + isCut = (strcmp(pdata, "cut") == 0 ? true : false); + paths = fm_path_list_new_from_uri_list(eol + 1); + } + } + + if(!paths && data->hasUrls()) { + // The KDE way + paths = Fm::pathListFromQUrls(data->urls()); + QByteArray cut = data->data("x-kde-cut-selection"); + + if(!cut.isEmpty() && cut.at(0) == '1') + isCut = true; + } + + if(paths) { + if(isCut) + FileOperation::moveFiles(paths, destPath, parent); + else + FileOperation::copyFiles(paths, destPath, parent); + + fm_path_list_unref(paths); + } +} + +void copyFilesToClipboard(FmPathList* files) { + QClipboard* clipboard = QApplication::clipboard(); + QMimeData* data = new QMimeData(); + char* urilist = fm_path_list_to_uri_list(files); + // Gnome, LXDE, and XFCE + data->setData("x-special/gnome-copied-files", (QString("copy\n") + urilist).toUtf8()); + // The KDE way + data->setData("text/uri-list", urilist); + // data.setData("x-kde-cut-selection", "0"); + g_free(urilist); + clipboard->setMimeData(data); +} + +void cutFilesToClipboard(FmPathList* files) { + QClipboard* clipboard = QApplication::clipboard(); + QMimeData* data = new QMimeData(); + char* urilist = fm_path_list_to_uri_list(files); + // Gnome, LXDE, and XFCE + data->setData("x-special/gnome-copied-files", (QString("cut\n") + urilist).toUtf8()); + // The KDE way + data->setData("text/uri-list", urilist); + data->setData("x-kde-cut-selection", "1"); + g_free(urilist); + clipboard->setMimeData(data); +} + +void renameFile(FmFileInfo *file, QWidget *parent) { + FmPath* path = fm_file_info_get_path(file); + FilenameDialog dlg(parent); + dlg.setWindowTitle(QObject::tr("Rename File")); + dlg.setLabelText(QObject::tr("Please enter a new name:")); + // FIXME: what's the best way to handle non-UTF8 filename encoding here? + QString old_name = QString::fromLocal8Bit(fm_path_get_basename(path)); + dlg.setTextValue(old_name); + + if(fm_file_info_is_dir(file)) // select filename extension for directories + dlg.setSelectExtension(true); + + if(dlg.exec() != QDialog::Accepted) + return; + + QString new_name = dlg.textValue(); + + if(new_name == old_name) + return; + + GFile* gf = fm_path_to_gfile(path); + GFile* parent_gf = g_file_get_parent(gf); + GFile* dest = g_file_get_child(G_FILE(parent_gf), new_name.toLocal8Bit().data()); + g_object_unref(parent_gf); + + GError* err = NULL; + if(!g_file_move(gf, dest, + GFileCopyFlags(G_FILE_COPY_ALL_METADATA | + G_FILE_COPY_NO_FALLBACK_FOR_MOVE | + G_FILE_COPY_NOFOLLOW_SYMLINKS), + NULL, /* make this cancellable later. */ + NULL, NULL, &err)) { + QMessageBox::critical(parent, QObject::tr("Error"), err->message); + g_error_free(err); + } + + g_object_unref(dest); + g_object_unref(gf); +} + +// templateFile is a file path used as a template of the new file. +void createFileOrFolder(CreateFileType type, FmPath* parentDir, FmTemplate* templ, QWidget* parent) { + QString defaultNewName; + QString prompt; + QString dialogTitle = type == CreateNewFolder ? QObject::tr("Create Folder") + : QObject::tr("Create File"); + + switch(type) { + case CreateNewTextFile: + prompt = QObject::tr("Please enter a new file name:"); + defaultNewName = QObject::tr("New text file"); + break; + + case CreateNewFolder: + prompt = QObject::tr("Please enter a new folder name:"); + defaultNewName = QObject::tr("New folder"); + break; + + case CreateWithTemplate: { + FmMimeType* mime = fm_template_get_mime_type(templ); + prompt = QObject::tr("Enter a name for the new %1:").arg(QString::fromUtf8(fm_mime_type_get_desc(mime))); + defaultNewName = QString::fromUtf8(fm_template_get_name(templ, NULL)); + } + break; + } + +_retry: + // ask the user to input a file name + bool ok; + QString new_name = QInputDialog::getText(parent, dialogTitle, + prompt, + QLineEdit::Normal, + defaultNewName, + &ok); + + if(!ok) + return; + + GFile* parent_gf = fm_path_to_gfile(parentDir); + GFile* dest_gf = g_file_get_child(G_FILE(parent_gf), new_name.toLocal8Bit().data()); + g_object_unref(parent_gf); + + GError* err = NULL; + switch(type) { + case CreateNewTextFile: { + GFileOutputStream* f = g_file_create(dest_gf, G_FILE_CREATE_NONE, NULL, &err); + if(f) { + g_output_stream_close(G_OUTPUT_STREAM(f), NULL, NULL); + g_object_unref(f); + } + break; + } + case CreateNewFolder: + g_file_make_directory(dest_gf, NULL, &err); + break; + case CreateWithTemplate: + fm_template_create_file(templ, dest_gf, &err, false); + break; + } + g_object_unref(dest_gf); + + if(err) { + if(err->domain == G_IO_ERROR && err->code == G_IO_ERROR_EXISTS) { + g_error_free(err); + err = NULL; + goto _retry; + } + + QMessageBox::critical(parent, QObject::tr("Error"), err->message); + g_error_free(err); + } +} + +uid_t uidFromName(QString name) { + uid_t ret; + if(name.isEmpty()) + return -1; + if(name.at(0).digitValue() != -1) { + ret = uid_t(name.toUInt()); + } + else { + struct passwd* pw = getpwnam(name.toLatin1()); + // FIXME: use getpwnam_r instead later to make it reentrant + ret = pw ? pw->pw_uid : -1; + } + + return ret; +} + +QString uidToName(uid_t uid) { + QString ret; + struct passwd* pw = getpwuid(uid); + + if(pw) + ret = pw->pw_name; + else + ret = QString::number(uid); + + return ret; +} + +gid_t gidFromName(QString name) { + gid_t ret; + if(name.isEmpty()) + return -1; + if(name.at(0).digitValue() != -1) { + ret = gid_t(name.toUInt()); + } + else { + // FIXME: use getgrnam_r instead later to make it reentrant + struct group* grp = getgrnam(name.toLatin1()); + ret = grp ? grp->gr_gid : -1; + } + + return ret; +} + +QString gidToName(gid_t gid) { + QString ret; + struct group* grp = getgrgid(gid); + + if(grp) + ret = grp->gr_name; + else + ret = QString::number(gid); + + return ret; +} + +int execModelessDialog(QDialog* dlg) { + // FIXME: this does much less than QDialog::exec(). Will this work flawlessly? + QEventLoop loop; + QObject::connect(dlg, &QDialog::finished, &loop, &QEventLoop::quit); + // DialogExec does not seem to be documented in the Qt API doc? + // However, in the source code of QDialog::exec(), it's used so let's use it too. + dlg->show(); + (void)loop.exec(QEventLoop::DialogExec); + return dlg->result(); +} + +// check if GVFS can support this uri scheme (lower case) +// NOTE: this does not work reliably due to some problems in gio/gvfs and causes bug lxde/lxqt#512 +// https://github.com/lxde/lxqt/issues/512 +// Use uriExists() whenever possible. +bool isUriSchemeSupported(const char* uriScheme) { + const gchar * const * schemes = g_vfs_get_supported_uri_schemes(g_vfs_get_default()); + if(Q_UNLIKELY(schemes == NULL)) + return false; + for(const gchar * const * scheme = schemes; *scheme; ++scheme) + if(strcmp(uriScheme, *scheme) == 0) + return true; + return false; +} + +// check if the URI exists. +// NOTE: this is a blocking call possibly involving I/O. +// So it's better to use it in limited cases, like checking trash:// or computer://. +// Avoid calling this on a slow filesystem. +// Checking "network:///" is very slow, for example. +bool uriExists(const char* uri) { + GFile* gf = g_file_new_for_uri(uri); + bool ret = (bool)g_file_query_exists(gf, NULL); + g_object_unref(gf); + return ret; +} + + +} // namespace Fm diff --git a/src/utilities.h b/src/utilities.h new file mode 100644 index 0000000..b6a3b35 --- /dev/null +++ b/src/utilities.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2013 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef FM_UTILITIES_H +#define FM_UTILITIES_H + +#include "libfmqtglobals.h" +#include +#include +#include +#include +#include + +class QDialog; + +namespace Fm { + +LIBFM_QT_API FmPathList* pathListFromQUrls(QList urls); + +LIBFM_QT_API void pasteFilesFromClipboard(FmPath* destPath, QWidget* parent = 0); + +LIBFM_QT_API void copyFilesToClipboard(FmPathList* files); + +LIBFM_QT_API void cutFilesToClipboard(FmPathList* files); + +LIBFM_QT_API void renameFile(FmFileInfo* file, QWidget* parent = 0); + +enum CreateFileType { + CreateNewFolder, + CreateNewTextFile, + CreateWithTemplate +}; + +LIBFM_QT_API void createFileOrFolder(CreateFileType type, FmPath* parentDir, FmTemplate* templ = NULL, QWidget* parent = 0); + +LIBFM_QT_API uid_t uidFromName(QString name); + +LIBFM_QT_API QString uidToName(uid_t uid); + +LIBFM_QT_API gid_t gidFromName(QString name); + +LIBFM_QT_API QString gidToName(gid_t gid); + +LIBFM_QT_API int execModelessDialog(QDialog* dlg); + +// NOTE: this does not work reliably due to some problems in gio/gvfs +// Use uriExists() whenever possible. +LIBFM_QT_API bool isUriSchemeSupported(const char* uriScheme); + +LIBFM_QT_API bool uriExists(const char* uri); + +} + +#endif // FM_UTILITIES_H diff --git a/src/utilities_p.h b/src/utilities_p.h new file mode 100644 index 0000000..7852f99 --- /dev/null +++ b/src/utilities_p.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2014 - 2015 Hong Jen Yee (PCMan) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __UTILITS_P_H__ +#define __UTILITS_P_H__ + +#include +#include +#include + +namespace Fm { + +// private class used in internal implementation +class FilenameDialog : public QInputDialog { + Q_OBJECT +public: + FilenameDialog(QWidget* parent = 0, Qt::WindowFlags flags = 0): + QInputDialog(parent, flags), + selectExtension_(false) { + } + + virtual void showEvent(QShowEvent * event) { + QWidget::showEvent(event); + if(!selectExtension_) // dot not select filename extension + QTimer::singleShot(0, this, SLOT(initSelection())); + } + + bool selectExtension() const { + return selectExtension_; + } + + void setSelectExtension(bool value) { + selectExtension_ = value; + } + +private Q_SLOTS: + // do not select filename extensions + void initSelection() { + // find the QLineEdit child widget + QLineEdit* lineEdit = findChild(); + if(lineEdit) { + QString filename = lineEdit->text(); + if(!filename.isEmpty()) { + // only select filename part without extension name. + int ext = filename.lastIndexOf('.'); + if(ext != -1) { + // add special cases for tar.gz, tar.bz2, and other tar.* files + if(filename.leftRef(ext).endsWith(".tar")) + ext -= 4; + // FIXME: should we also handle other special cases? + lineEdit->setSelection(0, ext); + } + } + } + } + +private: + bool selectExtension_; +}; + +} // namespace Fm + +#endif